
Виджет Locket – это инструмент для мгновенного обмена фотографиями с близкими прямо с домашнего экрана. В отличие от стандартных галерей, он интегрируется с AppWidgetProvider и обновляется в реальном времени через WorkManager. Для реализации потребуется Android SDK не ниже API 21 и базовые знания Kotlin или Java.
Первый шаг – настройка AndroidManifest.xml. Добавьте разрешение на работу с камерой и хранилищем, а также объявите виджет через тег <receiver> с указанием класса-наследника AppWidgetProvider. Пример минимальной конфигурации:
<receiver android:name=".LocketWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/locket_widget_info" />
</receiver>
Создайте файл res/xml/locket_widget_info.xml с параметрами виджета. Укажите минимальные размеры (например, 180dp × 180dp), частоту обновления (рекомендуется 3600000 мс – 1 час) и начальную разметку. Для динамического контента используйте RemoteViews с привязкой к ImageView и обработчиками кликов через PendingIntent.
В классе LocketWidgetProvider переопределите метод onUpdate(). Здесь загружайте фотографии из локального хранилища или облака (например, Firebase Storage) с помощью Glide или Coil. Для оптимизации памяти кешируйте изображения в LruCache и обновляйте виджет через AppWidgetManager.getInstance(context).updateAppWidget().
Для фоновой синхронизации используйте WorkManager с периодическим запросом PeriodicWorkRequest. Настройте ограничения по сети (setRequiredNetworkType(NetworkType.CONNECTED)) и батарее (setRequiresBatteryNotLow(true)). В воркере реализуйте логику проверки новых фотографий и отправки уведомлений через NotificationManager.
Тестируйте виджет на устройствах с разными версиями Android (особое внимание к Android 12+, где требуется явное разрешение на доступ к фото). Для отладки используйте adb shell dumpsys appwidget и проверяйте лог обновлений через Logcat с фильтром AppWidgetManager.
Подготовка среды разработки и необходимых инструментов

Для разработки виджета Locket на Android потребуется установить и настроить Android Studio версии 2023.2.1 (Giraffe) или новее. Эта версия включает в себя Android Gradle Plugin 8.2.0 и Kotlin 1.9.0, необходимые для корректной работы с Jetpack Compose и современными API виджетов. Скачайте дистрибутив с официального сайта, выбрав пакет для вашей ОС – Windows (64-bit), macOS (Intel/Apple Silicon) или Linux (DEB/RPM).
После установки Android Studio настройте SDK Manager. Убедитесь, что установлены следующие компоненты:
- Android SDK Platform 34 (API 34, Android 14) – целевая платформа для виджета;
- Android SDK Build-Tools 34.0.0 – инструменты сборки;
- NDK (Side by side) 26.1.10909125 – если планируется интеграция нативного кода;
- Android Emulator и HAXM (для Intel) или Hypervisor Framework (для Apple Silicon) – для тестирования.
Для работы с виджетами на основе Jetpack Glance добавьте зависимости в файл build.gradle (Module: app):
dependencies {
implementation "androidx.glance:glance-appwidget:1.0.0"
implementation "androidx.glance:glance-material3:1.0.0"
implementation "androidx.compose.ui:ui-tooling:1.6.0"
}
Обновите compileSdk до 34 и targetSdk до 34 в том же файле. Синхронизируйте проект после изменений.
Настройте эмулятор для тестирования виджета. Создайте виртуальное устройство с Android 14 (API 34) и разрешением экрана не менее 1080×2340 (например, Pixel 7). Включите опцию «Enable hardware keyboard» для удобства отладки. Для проверки работы виджета на реальных устройствах подключите смартфон с Android 8.0+ в режиме USB-отладки.
Установите ADB (Android Debug Bridge) для взаимодействия с устройствами. Проверьте его работоспособность командой adb devices в терминале. Если устройство не отображается, обновите драйверы через Device Manager (Windows) или выполните brew install android-platform-tools (macOS).
Для работы с изображениями и иконками виджета используйте Android Asset Studio или Figma с плагином Android Export. Создайте ресурсы в следующих размерах:
- mdpi: 48×48 px (базовый размер);
- hdpi: 72×72 px;
- xhdpi: 96×96 px;
- xxhdpi: 144×144 px;
- xxxhdpi: 192×192 px.
Поместите иконки в папку res/drawable-*/ с именем ic_locket_widget.png. Для адаптивных иконок используйте Adaptive Icon Generator из Android Studio.
Настройте ProGuard для оптимизации APK. В файл proguard-rules.pro добавьте правила для Glance и Compose:
-keep class androidx.glance.** { *; }
-keep class androidx.compose.runtime.** { *; }
Включите minifyEnabled true и shrinkResources true в build.gradle для релизной сборки. Проверьте корректность работы виджета после обфускации.
Для отладки виджета используйте Layout Inspector в Android Studio. Запустите приложение на устройстве, откройте Tools → Layout Inspector и выберите процесс виджета. Инструмент покажет иерархию компонентов, размеры и отступы, что поможет выявить проблемы с отображением. Для логгирования используйте Log.d("LocketWidget", "message") и фильтр по тегу в Logcat.
Настройка проекта Android Studio для работы с виджетами

В файле AndroidManifest.xml зарегистрируйте виджет с помощью тега <receiver>. Укажите атрибуты: android:name=".YourWidgetProvider", android:label="Имя виджета" и android:exported="false". Внутри тега добавьте <intent-filter> с действием android.appwidget.action.APPWIDGET_UPDATE. Задайте метаданные через <meta-data> с указанием ресурса конфигурации: android:name="android.appwidget.provider" и android:resource="@xml/your_widget_info".
Создайте XML-файл конфигурации виджета в папке res/xml. Пример структуры: <appwidget-provider> с параметрами minWidth="110dp", minHeight="40dp", updatePeriodMillis="1800000" (30 минут), initialLayout="@layout/widget_layout". Для адаптивных виджетов используйте targetCellWidth="3" и targetCellHeight="1" вместо фиксированных размеров. Укажите resizeMode="horizontal|vertical", если виджет должен масштабироваться.
Настройте minSdkVersion в build.gradle не ниже 21 – это минимальная версия Android с поддержкой всех функций Glance. Для работы с RemoteViews (альтернатива Glance) достаточно API 16, но современные возможности, такие как динамические темы и Jetpack Compose, требуют API 26+. Проверьте совместимость с целевой аудиторией перед выбором версии.
Создайте класс-наследник GlanceAppWidget или AppWidgetProvider в зависимости от выбранного подхода. Для Glance переопределите метод Content(), возвращающий композицию с UI виджета. Пример: @Composable fun Content() { Text("Данные") }. Для RemoteViews используйте onUpdate(), где создавайте экземпляр RemoteViews(packageName, R.layout.widget_layout) и обновляйте его через AppWidgetManager.
Для тестирования виджета добавьте его на главный экран через Android Studio Device Manager. Выберите эмулятор с API 30+ – на более старых версиях возможны баги с отображением. Используйте команду adb shell am broadcast -a android.appwidget.action.APPWIDGET_UPDATE для принудительного обновления. Логируйте события в onUpdate() с помощью Log.d("Widget", "Обновление") для отладки.
Создание базовой структуры виджета в файле макета XML
Файл макета виджета Locket размещается в директории res/layout/ и должен иметь уникальное имя, например, widget_locket.xml. Начните с корневого элемента <appwidget-provider>, но для визуальной части используйте <RelativeLayout> или <FrameLayout> в качестве контейнера. Эти контейнеры обеспечивают гибкость при позиционировании элементов и адаптируются к разным размерам виджетов.
Минимальная ширина и высота виджета задаются в appwidget-provider через атрибуты minWidth и minHeight. Для Locket рекомендуется использовать значения 110dp и 110dp соответственно – это стандартный размер для квадратных виджетов на большинстве лаунчеров. Учтите, что лаунчеры округляют размеры до ближайшего кратного 70dp для ширины и 110dp для высоты.
Внутри контейнера разместите основные элементы:
<ImageView>– для отображения фотографии. Задайте атрибутыandroid:layout_width="match_parent"иandroid:layout_height="match_parent", чтобы изображение заполняло весь виджет. Используйтеandroid:scaleType="centerCrop"для корректного обрезания фотографий с разными пропорциями.<TextView>– для подписи или времени. Разместите его в нижней части виджета с помощьюandroid:layout_alignParentBottom="true". Задайтеandroid:background="#80000000"для полупрозрачного фона иandroid:padding="8dp"для отступов.
Для динамического обновления контента добавьте идентификаторы к элементам:
android:id="@+id/widget_image"дляImageView.android:id="@+id/widget_text"дляTextView.
Эти идентификаторы потребуются в классе AppWidgetProvider для доступа к элементам через RemoteViews.
Оптимизируйте производительность, ограничив количество вложенных контейнеров. Если требуется сложная компоновка, используйте <merge> для уменьшения уровня вложенности. Пример:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="@+id/widget_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/widget_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#80000000"
android:padding="8dp"/>
</merge>
Для поддержки темной темы добавьте альтернативный файл макета в res/layout-night/. В нем измените цвета фона и текста, например, android:background="#80FFFFFF" для светлого текста на темном фоне. Убедитесь, что все идентификаторы элементов совпадают с основным макетом.
Проверьте макет на разных размерах экрана, используя инструмент Layout Inspector в Android Studio. Запустите виджет на эмуляторе с разными разрешениями (например, 480×800, 1080×1920) и убедитесь, что элементы не выходят за границы виджета. При необходимости скорректируйте отступы и размеры с помощью dimens.xml.
Для анимаций или интерактивности добавьте обработчики событий через RemoteViews.setOnClickPendingIntent() в коде AppWidgetProvider. В макете XML достаточно указать android:clickable="true" для интерактивных элементов, например, для ImageView, чтобы открывать полноэкранное изображение при нажатии.
Реализация логики обновления виджета через класс AppWidgetProvider
Класс AppWidgetProvider – наследник BroadcastReceiver, обрабатывающий системные события виджета: обновление, удаление, включение и отключение. Для реализации логики обновления переопределите метод onUpdate(), который вызывается при добавлении виджета на экран или по расписанию через updatePeriodMillis в метаданных. Минимальный интервал обновления – 30 минут, меньшие значения игнорируются системой.
Внутри onUpdate() создайте RemoteViews для модификации UI виджета. Пример базовой структуры:
| Этап | Код | Назначение |
|---|---|---|
| 1. Получение ID виджетов | int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); |
Массив идентификаторов всех экземпляров виджета |
| 2. Инициализация RemoteViews | RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); |
Связь с XML-разметкой виджета |
| 3. Обновление контента | views.setTextViewText(R.id.widget_text, "Новые данные"); |
Динамическое изменение текста или изображений |
| 4. Применение изменений | appWidgetManager.updateAppWidget(appWidgetIds, views); |
Отправка обновленного UI на экран |
Для принудительного обновления извне (например, по нажатию кнопки в приложении) используйте AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, views). Избегайте частых вызовов – система ограничивает частоту обновлений для экономии батареи. Альтернатива: WorkManager с периодическими задачами для фоновых обновлений.
Обработка событий onEnabled() и onDisabled() позволяет оптимизировать ресурсы. В onEnabled() запускайте сервисы или планировщики, необходимые для работы виджета (например, AlarmManager для обновлений чаще 30 минут). В onDisabled() – освобождайте ресурсы, отменяйте задачи. Пример:
@Override
public void onEnabled(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, WidgetUpdateReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
alarmManager.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis(), 15 * 60 * 1000, pendingIntent);
}
Для передачи данных между приложением и виджетом используйте SharedPreferences или базу данных. Храните ключевые параметры (например, ID пользователя для загрузки контента) в SharedPreferences с режимом MODE_PRIVATE. При обновлении виджета читайте данные из хранилища и передавайте их в RemoteViews. Избегайте хранения больших объемов данных – виджеты ограничены по памяти.
