Лучший информационный виджет: Google At a Glance (Free)
At a Glance является частью приложения Google, установленного на каждом телефоне Android. Это однорядный виджет, который обновляется в течение дня и отображает такие кусочки информации как дорожный трафик или вашу следующую встречу. А если ничего особенного не происходит, то информацию о дате и погоде.
Transparent weather clock
Отличное приложение для смартфона – виджет Transparent weather clock. Это многофункциональное решение, включающее в себя удобные прозрачные часы с данными о погоде. Виджет имеет широкий набор настроек, что позволяет каждому пользователю выбрать свой стиль и формат его отображения на рабочем столе экрана гаджета.
Приложение доступно бесплатно, но в нем присутствует реклама. Если вы хотите ее отключить, то нужно приобрести платную версию. Как и многие виджеты для Андроид на русском языке, это приложение мультиязычное. Желаемый язык отображения можно настроить в меню. Программа автоматически определяет место вашего расположения и всегда предоставляет актуальные данные о погоде, показывает местное время, а также точное время восхода и заката солнца. Кроме этого, виджет показывает активные будильники, события календаря и много другой нужной информации.
Лучший погодный виджет: Overdrop Weather (Free) | Overdrop Pro ($4)
Overdrop — относительно новичок в постоянно расширяющемся жанре погодных приложений для Android. Смотрится он лучше всех и предлагает удивительно точные поминутные прогнозы от службы DarkSky.
Также большое внимание уделяется виджетам. Вы получаете 21 бесплатный виджет, плюс еще 17 при обновлении через покупку внутри приложения. Они охватывают практически все стили, о которых вы только могли подумать.
Лучший виджет с часами и будильником: Chronus (Free, покупки в приложении)
Каждому экрану нужны отлично выглядящие виджеты часов. Chronus дает тебе полдюжины таких. Он предлагает на выбор цифровые и аналоговые часы с дополнительными функциями отображения ежедневника, погоды, акций или новостной ленты. Он даже имеет виджет, совместимый с Google Fit, который выводит ваши ежедневные шаги на главный экран.
Как добавить виджеты на Android – пошаговая инструкция
- Длительно нажмите пустое место на главном экране, и вы увидите контекстное меню. В контекстном меню выберите « Виджеты» .
- Теперь выберите виджет из меню, которое вы хотите добавить на домашний экран. Нажмите и удерживайте виджет и поместите его на место, выделенное на главном экране.
- Как только виджет будет на месте, вы можете отрегулировать размер, нажав на виджет и перетащив его из угла.
- Если по какой-либо причине вам не нравится виджет, вы можете просто удалить его, перетащив его в окно «Удалить».
Имейте в виду, что процесс добавления или удаления виджетов незначительно отличается от одного скина Android к другому, но основные шаги остаются прежними. Теперь, когда вы добавили виджет на главный экран, вы можете изменить некоторые настройки, нажав на виджет.
Этот параметр доступен не для всех виджетов, поскольку не все они поддерживают настройку.
Лучший виджет заметок: Google Keep (Free)
В Play Store вы найдете множество замечательных приложений для заметок с виджетами. Но я рекомендую попробовать Google Keep.
Он предлагает два виджета. Одна из них представляет собой простую панель быстрых клавиш, которая позволяет создавать основные заметки, списки, голосовые заметки, рукописные заметки или заметки с фотографиями. Другой позволяет прикреплять заметки к главному экрану. Он идеально подходит для списков покупок или чего-то подобного.
Лучшие To-Do виджет: TickTick (Free, подписки в приложении)
TickTick — это потрясающее приложение для управления задачами, которое поставляется с девятью виджетами, гарантирующими вам значительно более высокую производительность. Они варьируются от исчерпывающего многостраничного перечня дел до трехдневного представления повестки дня и базового контрольного перечня вопросов. Есть даже виджет таймера Pomodoro.
Использование виджета со списком в приложении
Post Views: 3 584
Виджеты (Widgets) — это такие мини-приложения. которые могут быть встроены в главный экран устройства и выводить полезную информацию из самого приложения или даже взаимодействовать с ним, выполняя какие-либо операции. Поэтому виджеты являются очень важной частью приложения, к тому же они удобны в использовании: например, у многих музыкальных плееров есть свой виджет, который позволяет менять треки или ставить их на паузу без запуска приложения.
В ранних версиях Android виджеты могли отображать только такие простые элементы, как TextView, ImageView и так далее. Однако сейчас их возможности стали намного больше, теперь можно использовать и более сложные ListView, GridView и StackView, что позволяет показывать в виджетах больше самой разной информации.
В этой статье мы рассмотрим, как добавить список ListView в виджет и обработать нажатия на его элементы. Делать это будем на примере виджета для приложения Менеджер паролей от Wi-Fi сетей.
Для начала нужно создать XML-файлы разметки и метаданных.
Файл разметки будет определять внешний вид нашего виджета и расположение элементов на нём. В данном случае виджет будет состоят из заголовка с названием приложения и ListView, который будет содержать список активных сетей. Добавим в папку res/layout файл widget_network.xml со следующим кодом:
Поскольку элемент в списке содержит разные данные, для него тоже сделана отдельная разметка в файле widget_list_item.xml, также расположенном в res/layout, которая представляет собой слегка изменённую разметку элемента списка RecyclerView из приложения.
Основными данными для показа здесь являются SSID сети, пароль к ней, дата добавления, а также отметка о том, скрыта сеть или нет. При раскрытии элемента будут отображаться дополнительные кнопки, позволяющие подключиться к сети, скопировать пароль или поделиться данными о ней с другими людьми.
Теперь нужно создать файл с метаданными. Для этого в папке res/xml создадим файл network_widget.xml.
В этом файле мы указываем разметку виджета, которую мы добавили ранее, а также размеры виджета, картинку, которая будет отображаться в списке виджетов, время обновления и возможность менять размер виджета.
Теперь, закончив с внешним видом виджета, можно приступить к реализации работы со списком. Для этого нам понадобится создать 3 класса: WidgetRemoteViewsFactory, WidgetRemoteViewsService, WidgetProvider. Логику их взаимодействия можно описать следующими словами: WidgetProvider при обновлении виджета будет посылать интент в WidgetRemoteViewsService, который будет возвращать обратно экземпляр WidgetRemoteViewsFactory. О том, как это устроено, будет рассмотрено ниже.
Класс WidgetProvider наследует от AppWidgetProvider, его задачей является реализация жизненного цикла виджета. Создадим класс WidgetProvider, содержащий следующий код:
public class WidgetProvider extends AppWidgetProvider { private static final String TAG = «PROVIDER»; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); for (int widgetId : appWidgetIds) { updateWidget(context, appWidgetManager, widgetId); } } private void updateWidget(Context context, AppWidgetManager appWidgetManager, int widgetId) { RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_network); appWidgetManager.updateAppWidget(widgetId, views); } public static void sendRefreshBroadcast(Context context) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.setComponent(new ComponentName(context, WidgetProvider.class)); context.sendBroadcast(intent); } @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (BuildConfig.DEBUG) Log.d(TAG, action); if (!TextUtils.isEmpty(action)) { if (action.equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE)) { AppWidgetManager manager = AppWidgetManager.getInstance(context); ComponentName cn = new ComponentName(context, WidgetProvider.class); manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(cn), R.id.widgetList); } } super.onReceive(context, intent); } }
Метод onUpdate() вызывается, когда происходит обновление виджетов, при этом в параметры передаются контекст приложения, объект AppWidgetManager и ID всех виджетов, которые нужно обновить.
Если посмотреть исходный код класса AppWidgetProvider, то можно увидеть, что он наследует от класса BroadcastReceiver, поэтому он может принимать широковещательные сообщения от приложения. Метод onReceive() принимает эти сообщения и обрабатывает их в зависимости от того, что нужно разработчику.
Чтобы провайдер принимал сообщения, нужно зарегистрировать его в манифесте. Для этого в файл AndroidManifect.xml в добавим следующий код:
В указываем, что хотим получать сообщения об обновлении виджета, а в указываем XML-файл с метаданными, который мы создали ранее.
Класс WidgetRemoteViewsService выступает посредником между WidgetProvider и WidgetRemoteViewsFactory. Этот сервис должен принимать интент от WidgetProvider и возвращать ему объект WidgetRemoteViewsFactory, который заполняет элемент списка в виджете данными. Создадим класс WidgetRemoteViewsService, наследующий от RemoteViewsService.
public class WidgetRemoteViewsService extends RemoteViewsService { @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { return new WidgetRemoteViewsFactory(this.getApplicationContext(), intent); } }
Как и любой другой сервис, его нужно зарегистрировать в манифестве приложения. Для этого в файл AndroidManifect.xml внутри добавим следующий код:
Разрешение android.permission.BIND_REMOTEVIEWS позволяет системе привязать сервис с целью добавления представления виджета для каждого элемента и не позволяет другим приложения получать доступ к данным виджета.
Задачей класса WidgetRemoteViewsFactory является заполнение списка в виджете данными. Иными словами, здесь он выступает как адаптер ListView. Для того, чтобы работать со списком, класс должен реализовывать интерфейс RemoteViewsService.RemoteViewsFactory. Создадим класс WidgetRemoteViewsFactory со следующим кодом:
public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { private Context mContext; private List list; private DateFormat dateFormat; private int mWidgetId; public WidgetRemoteViewsFactory(Context context, Intent intent) { mContext = context; mWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); } @Override public void onCreate() { list = new ArrayList<>(); String pattern = ((SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault())).toPattern(); dateFormat = new SimpleDateFormat(pattern, Locale.getDefault()); } @Override public void onDataSetChanged() { list.clear(); Set networkList = new HashSet<>(); String json = SP.getString(mContext, WIDGET_LIST, NULL); if (json != NULL) { networkList = SP.getWidgetList(json); } if (networkList != NULL) { list.addAll(networkList); } } @Override public void onDestroy() { } @Override public int getCount() { return list.size(); } @Override public RemoteViews getViewAt(int i) { WifiInfoWidget wi = list.get(i); RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_list_item); rv.setTextViewText(R.id.widget_SSID, wi.SSID); if (wi.hidden) { rv.setViewVisibility(R.id.widget_tv_dot, View.VISIBLE); rv.setViewVisibility(R.id.widget_tv_hide, View.VISIBLE); } else { rv.setViewVisibility(R.id.widget_tv_dot, View.INVISIBLE); rv.setViewVisibility(R.id.widget_tv_hide, View.INVISIBLE); } rv.setTextViewText(R.id.widget_password, wi.password); rv.setTextViewText(R.id.widget_date, dateFormat.format(wi.date)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { rv.setImageViewResource(R.id.widget_copy, R.drawable.ic_content_copy_black_24px); rv.setImageViewResource(R.id.widget_share, R.drawable.ic_share_black_24px); if (wi.widgetExpand) { rv.setImageViewResource(R.id.widget_arrow, R.drawable.ic_keyboard_arrow_up_black_24dp); } else { rv.setImageViewResource(R.id.widget_arrow, R.drawable.ic_keyboard_arrow_down_black_24dp); } } else { Drawable drawableCopy = VectorDrawableCompat.create(mContext.getResources(), R.drawable.ic_content_copy_black_24px, mContext.getTheme()); if (drawableCopy != NULL) { setDrawable(rv, R.id.widget_copy, drawableCopy); } Drawable drawableShare = VectorDrawableCompat.create(mContext.getResources(), R.drawable.ic_share_black_24px, mContext.getTheme()); if (drawableShare != NULL) { setDrawable(rv, R.id.widget_share, drawableShare); } Drawable drawableArrow; if (wi.widgetExpand) { drawableArrow = VectorDrawableCompat.create(mContext.getResources(), R.drawable.ic_keyboard_arrow_up_black_24dp, mContext.getTheme()); } else { drawableArrow = VectorDrawableCompat.create(mContext.getResources(), R.drawable.ic_keyboard_arrow_down_black_24dp, mContext.getTheme()); } if (drawableArrow != NULL) { setDrawable(rv, R.id.widget_arrow, drawableArrow); } } if (wi.widgetExpand) { rv.setViewVisibility(R.id.widget_more, View.VISIBLE); } else { rv.setViewVisibility(R.id.widget_more, View.GONE); } return rv; } @Override public RemoteViews getLoadingView() { return NULL; } @Override public int getViewTypeCount() { return 1; } @Override public long getItemId(int i) { return i; } @Override public boolean hasStableIds() { return true; } private void setDrawable(RemoteViews rv, int id, Drawable drawable) { Bitmap b = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(b); drawable.setBounds(0, 0, c.getWidth(), c.getHeight()); drawable.draw(c); rv.setImageViewBitmap(id, b); } }
Метод onCreate() вызывается при создании адаптера, здесь мы инициализируем объект List и формат даты для показа.
Метод onDataSetChanged() вызывается, когда адаптер обновил виджет. В этом методе забираем сети из SharedPreferences и заполняем им ранее инициализированный список.
Метод onDestroy() вызывается при удалении списка, здесь, если требуется, нужно реализовывать логику очистки.
Метод getCount() возвращает количество элементов в списке.
Метод getViewAt() здесь является самым важным, он выполняет заполнение элемента списка данными, затем возвращает в адаптер посредством сервиса готовый объект RemoteViews.
Метод getLoadingView() возвращает специальный объект View, если элементы списка ещё не успели создаться.
Метод getViewTypeCount() возвращает количество типов представлений в ListView. Поскольку представления в списке одинаковые, возвращаем 1.
Метод getItemId() возвращает ID элемента в выбранной позиции.
Метод hasStableIds() возвращает true, если один и тот же ID всегда относится к одному и тому же объекту.
Теперь нам нужно подключить к нашему провайдеру адаптер. Для этого добавим в метод updateWidget() класса WidgetProvider следующий код:
private void updateWidget(Context context, AppWidgetManager appWidgetManager, int widgetId) { RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_network);
setList(views, context, widgetId); appWidgetManager.updateAppWidget(widgetId, views); } private void setList(RemoteViews views, Context context, int widgetId) { Intent intent = new Intent(context, WidgetRemoteViewsService.class); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); views.setRemoteAdapter(R.id.widgetList, intent); }
Здесь с помощью метода setRemoteAdapter() мы устанавливаем адаптер списка, который подключается к сервису WidgetRemoteViewsService через специальный интент.
Теперь нам нужно сделать так, чтобы, при нажатии на кнопки в списке виджета, выполнялись определённые операции. Сложность здесь состоит в том, что при использовании коллекций не разрешается устанавливать PendingIntent на отдельные элементы. Поэтому воспользуемся методом setPendingIntentTemplate() для установки шаблона PendingIntent в коллекции, а отдельные элементы будут вызываться посредством метода setOnClickFillInIntent().
Для начала в методе updateWidget() класса WidgetProvider создадим шаблон для коллекции, который будет отправлять в onReceive() событие о нажатии.
public static final String ACTION_ON_ITEM_CLICK = «ON_MORE_CLICK»; public static final String COMMAND = «COMMAND»; public static final String MORE = «MORE»; public static final String CONNECT = «CONNECT»; public static final String COPY = «COPY»; public static final String SHARE = «SHARE»; public static final String ITEM = «ITEM»; … private void updateWidget(Context context, AppWidgetManager appWidgetManager, int widgetId) { … final Intent onItemClick = new Intent(context, WidgetProvider.class); onItemClick.setAction(ACTION_ON_ITEM_CLICK); onItemClick.setData(Uri.parse(onItemClick.toUri(Intent.URI_INTENT_SCHEME))); final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0, onItemClick, PendingIntent.FLAG_UPDATE_CURRENT); views.setPendingIntentTemplate(R.id.widgetList, onClickPendingIntent); appWidgetManager.updateAppWidget(widgetId, views); }
Затем в классе WidgetRemoteViewsFactory в методе getViewAt() добавим интенты при нажатии на кнопки.
@Override public RemoteViews getViewAt(int i) { … if (wi.widgetExpand) { rv.setViewVisibility(R.id.widget_more, View.VISIBLE);
rv.setOnClickFillInIntent(R.id.btn_widget_connect, createIntent(WidgetProvider.CONNECT, wi)); rv.setOnClickFillInIntent(R.id.widget_copy, createIntent(WidgetProvider.COPY, wi)); rv.setOnClickFillInIntent(R.id.widget_share, createIntent(WidgetProvider.SHARE, wi)); } else { rv.setViewVisibility(R.id.widget_more, View.GONE); } rv.setOnClickFillInIntent(R.id.widget_arrow, createIntent(WidgetProvider.MORE, wi)); return rv; } private Intent createIntent(String cmd, WifiInfoWidget wi) { Intent intent = new Intent(); intent.setAction(WidgetProvider.ACTION_ON_ITEM_CLICK); Bundle bundle = new Bundle(); bundle.putString(WidgetProvider.COMMAND, cmd); bundle.putSerializable(WidgetProvider.ITEM, wi); intent.putExtras(bundle); return intent; }
В объект Bundle кроме данных о сети мы также добавляем команду, по которой провайдер будет различать, нажатие на какую кнопку было совершено и какие действия нужно выполнить.
Нажатия на кнопки реализованы, теперь нужно их обработать. Вернёмся в класс WidgetProvider, в метод onReceive() добавим следующий код:
@Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (BuildConfig.DEBUG) Log.d(TAG, action); if (!TextUtils.isEmpty(action)) { if (action.equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE)) { AppWidgetManager manager = AppWidgetManager.getInstance(context); ComponentName cn = new ComponentName(context, WidgetProvider.class); manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(cn), R.id.widgetList); }
if (action.equals(ACTION_ON_ITEM_CLICK)) { parseItemClick(context, intent.getExtras()); } } super.onReceive(context, intent); } private void parseItemClick(Context context, Bundle bundle) { if (bundle != NULL) { String command = bundle.getString(COMMAND); if (!TextUtils.isEmpty(command)) { if (BuildConfig.DEBUG) Log.d(TAG, command); switch (command) { case MORE: { WifiInfoWidget wi = (WifiInfoWidget) bundle.getSerializable(ITEM); if (wi != NULL) { wi.widgetExpand = !wi.widgetExpand; Set networkList = new HashSet<>(); String json = SP.getString(context, WIDGET_LIST, NULL); if (json != NULL) { networkList = SP.getWidgetList(json); } if (networkList != NULL) { for (WifiInfoWidget wifiInfo : networkList) { if (wifiInfo.equals(wi)) { wifiInfo.widgetExpand = wi.widgetExpand; SP.saveWidgetList(context, networkList); sendRefreshBroadcast(context); break; } } } } break; } case CONNECT: { WifiInfoWidget wi = (WifiInfoWidget) bundle.getSerializable(ITEM); if (wi != NULL) { App.selectContent(«widget», «подключить сеть»); WifiManager wifiManager = (WifiManager) context.getApplicationContext() .getSystemService(Context.WIFI_SERVICE); if (wifiManager == NULL) { return; } Toast.makeText(context, R.string.wifi_changing_network, Toast.LENGTH_SHORT).show(); Set networkList = new HashSet<>(); String json = SP.getString(context, MAIN_LIST, NULL); if (json != NULL) { networkList = SP.getList(json); } if (networkList != NULL) { for (WifiInfo wifiInfo : networkList) { if (wifiInfo.SSID.equals(wi.SSID) && wifiInfo.password.equals(wi.password) && wifiInfo.hidden == wi.hidden) { WifiConfigManager wcf = new WifiConfigManager(wifiManager); if (wcf.getStatus().toString().equals(«PENDING»)) wcf.execute(wifiInfo); break; } } } } break; } case COPY: { WifiInfoWidget wi = (WifiInfoWidget) bundle.getSerializable(ITEM); if (wi != NULL) { App.selectContent(«widget», «копировать»); Tools.CopyToClipboard(context, wi.password); Toast.makeText(context, R.string.Copy_value, Toast.LENGTH_LONG).show(); } break; } case SHARE: { WifiInfoWidget wi = (WifiInfoWidget) bundle.getSerializable(ITEM); if (wi != NULL) { App.selectContent(«widget», «строка логин-пароль»); String s = «SSID : » + wi.SSID + «\nPassword : » + wi.password; Intent i = new Intent(); i.setAction(Intent.ACTION_SEND); i.putExtra(Intent.EXTRA_TEXT, s); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); i.setType(«test/plain»); context.startActivity(i); } break; } } } } }
Таким образом мы можем легко обрабатывать нажатия на различные элементы списка.
Осталось малое: посылать в виджет широковещательное сообщение, которое будет запускать обновление при добавлении/удалении элемента из списка в приложении. Для этого в классе WidgetProvider добавим метод sendRefreshBroadcast(), который будет отправлять в onReceive() сообщение об обновлении виджета, что затем вызовет всю цепочку WidgetProvider — WidgetRemoteViewsService — WidgetRemoteViewsFactory — WidgetProvider.
public static void sendRefreshBroadcast(Context context) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.setComponent(new ComponentName(context, WidgetProvider.class)); context.sendBroadcast(intent); }
Метод этот будем вызывать в классе главной активности в местах, где происходит изменение списка.
WidgetProvider.sendRefreshBroadcast(mainView.getContext());
На этом всё. Результат того, как работает наш виджет, вы можете увидеть ниже.
Лучший виджет акций: Investing.com Stock Exchange (Free, покупки в приложении)
Отслеживание акций на более чем 70 глобальных биржах.
Спасибо, что читаете! Подписывайтесь на мой канал в Telegram и Яндекс.Дзен. Только там последние обновления блога и новости мира информационных технологий.
Также, читайте меня в социальных сетях: Facebook, Twitter, VK, OK.
Респект за пост! Спасибо за работу!
Хотите больше постов? Узнавать новости технологий? Читать обзоры на гаджеты? Для всего этого, а также для продвижения сайта, покупки нового дизайна и оплаты хостинга, мне необходима помощь от вас, преданные и благодарные читатели. Подробнее о донатах читайте на специальной странице.
Есть возможность стать патроном, чтобы ежемесячно поддерживать блог донатом, или воспользоваться Яндекс.Деньгами, WebMoney, QIWI или PayPal:
Заранее спасибо! Все собранные средства будут пущены на развитие сайта. Поддержка проекта является подарком владельцу сайта.
Какой смартфон нужен для стабильной работы приложений
Если вы планируете использовать свой смартфон максимально функционально, не только для звонков и СМС, то нужно будет устанавливать достаточно большое количество виджетов и приложений. Все эти программы требуют определенных ресурсов. Это и производительность процессора, и оперативная память. Кроме того, нельзя забывать о том, что для установки приложения, его регулярных обновлений требуется определенный объем встроенной памяти телефона. Лучшие виджеты для Андроид имеют множество настроек, функций и привлекательный дизайн. Чтобы такие программы работали без зависаний и сбоев, смартфон должен иметь достаточно свободных ресурсов.
Итак, делаем вывод: нам требуется мощный и производительный смартфон с хорошим процессором, достаточным объемом оперативной и встроенной памяти. Кроме того неплохо бы иметь качественную камеру и другие полезные модули. Естественно, аппарат должен иметь отличный дисплей и стильный дизайн. И если у вас ограниченный бюджет, то обратите внимание на модели британской компании Wileyfox.
Это молодой бренд, который впервые вышел на рынок мобильных гаджетов в октябре 2020 года. Каждая модель компании получила характеристики и функции, наиболее востребованные среди пользователей. Это:
- Работа с двумя сим-картами;
- Поддержка сетей передачи данных 4G LTE;
- Стильный современный дизайн;
- Мощная аппаратная составляющая;
- Стабильно работающая прошивка операционной системы;
- Доступная цена.
Если вам нужен стильный, мощный, современный и недорогой смартфон, обратите внимание на модель Wileyfox Swift 2.