Пока Евгений Зобнин учит тебя защищать свой код от чужих глаз и шаловливых ручонок, я решил занять позицию хитрого разработчика, который всегда рад, скажем так, кое-чему поучиться у создателей снискавших определенную популярность приложений ;). В этой статье я расскажу, как я анализировал код чужих программ и нашел одну интересную недокументированную функцию для работы с виджетами приложений.
 

Виджет с настройкой при добавлении

Многие из нас видели экран с настройками, который появляется при добавлении виджета на экран устройства. Например, погодный виджет может спросить: «А для какого города показывать прогноз?» Таким образом можно очень гибко настроить несколько виджетов одного и того же приложения для отображения разной информации.

Чтобы сэкономить труд на рутинных операциях, Android Studio может за нас в пару кликов создать рабочую заготовку с настраиваемым виджетом. От нас требуется только проставить нужные галочки в очень информативном мастере. Запускается он так: New-Widget-AppWidget.

Простой мастер, все ясно без документации
Простой мастер, все ясно без документации

В результате его работы у нас в проекте появятся два новых класса, которые автоматически пропишутся в манифест:

<receiver android:name=".NewAppWidget">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
    </intent-filter>

    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/new_app_widget_info"/>
</receiver>

<activity android:name=".NewAppWidgetConfigureActivity">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
    </intent-filter>
</activity>

Первый класс отвечает за сам виджет, а второй представляет собой активность для его настройки, которая запускается как раз при его добавлении. Такую активность мы можем запускать в разные моменты, но будет не очень понятно, какой именно виджет сейчас настраивается. Так что момент самый удачный и лучше больше никаких мест для данной настройки не использовать. Этот пример создания виджета описан во всех уроках и доках. Код простой, и ты сам в нем разберешься. Меня же заинтересовала возможность выбора иконки приложения, реализованная через виджет. Такая функция реализована в приложении Wifi Analyzer.

Вот же виджет. Негусто, но все же выбор
Вот же виджет. Негусто, но все же выбор
Вот же виджет. Негусто, но все же выбор
Вот же виджет. Негусто, но все же выбор
Вот же виджет. Негусто, но все же выбор
 

Полезные и бесполезные виджеты

Поначалу я хотел просто рисовать иконку приложения в виджете. Это обосновано тем, что для добавления иконки на рабочий стол нужно добавить новое разрешение в манифест.

<uses-permission
    android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />

А так как пользователи не любят много полномочий у приложения и вдобавок при изменении разрешений в манифесте отключается механизм автообновления, то я старался сделать иконки через механизм виджетов. И как я ни упражнялся в дизайне, получить иконку один в один как оригинальная не выходило. Виной всему было непонимание работы системы. Виджет не предназначен для дублирования функциональности иконки, он может быть максимально на нее похож, но рисуются они по-разному, и на каждой прошивке свой лаунчер, рисующий рабочий стол с ярлыками и виджетами, как вздумается его разработчику.

Посмотрев чужое творчество, я поначалу был слегка обескуражен: у данного приложения виджет не имеет экрана настройки и всего лишь дублирует функциональность иконки, просто запуская приложение. На первый взгляд он совершенно бесполезен... Но когда я связался с авторами этого приложения, мне сказали, что виджет показывает количество непрочитанных писем. Такая функция есть в iOS.

На первый взгляд — ничего полезного... на второй, впрочем, чуть лучше
На первый взгляд — ничего полезного... на второй, впрочем, чуть лучше
На первый взгляд — ничего полезного... на второй, впрочем, чуть лучше
На первый взгляд — ничего полезного... на второй, впрочем, чуть лучше
На первый взгляд — ничего полезного... на второй, впрочем, чуть лучше

Правда, не совсем понятно, зачем крохотному виджету сделали возможность менять свой размер? Или почему не сделали настройку выбора почтового ящика при добавлении виджета, например?

Что-то тут не так
Что-то тут не так
 

Делаем иконки, как farproc

Раз не получилось сделать новую иконку через обычный виджет, нужно посмотреть, как поступают другие разработчики. В этом могут помочь разные инструменты, я обычно пользуюсь онлайн-декомпилятором или decompileandroid.com. Заглянув в манифест приложения Wifi Analyzer, я не нашел обычных описаний виджетов, как из примера выше. Но виджет-то был! И он как-то запускал экран с выбором иконки!

Методом глубокого поиска и научного тыка было установлено, что тут используется недокументированная функция. А в манифесте только и нужно, что добавить новую activity с особым фильтром:

<activity
    android:name="com.farproc.wifi.analyzer.CreateShortcutScreen"
    android:excludeFromRecents="true"
    android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
    android:noHistory="true">
    <intent-filter>
        <action android:name="android.intent.action.CREATE_SHORTCUT"/>
    </intent-filter>
</activity>

Нужный экран мы запустили, а дальше уже полная свобода творчества. Вот, к примеру, совсем понятный, «обградленный» код добавления иконки:

private Intent m1641a(int i) {
    Intent intent = new Intent();
    Parcelable intent2 = new Intent(this, MainScreen.class);
    intent.putExtra("android.intent.extra.shortcut.ICON_RESOURCE", ShortcutIconResource.fromContext(this, f1149c[i]));
    intent.putExtra("android.intent.extra.shortcut.INTENT", intent2);
    intent.putExtra("android.intent.extra.shortcut.NAME", getString(R.string.app_name));
    return intent;
}

Все настоящие имена методов изменены, но от констант никуда не денешься. Они и доносят нам суть происходящего.

 

Как включить мобильные данные с виджета?

У упомянутого выше автора есть приложение Data Enabler Widget. Функциональность его позволяет включать и выключать мобильный интернет прямо с рабочего стола посредством виджета без root-прав. Этим виджетом я пользовался на старом Samsung с версией Android 2.3, но на последних версиях он работать не хочет :).

В декомпилированном виде функция включения GPRS данных выглядит так:

private void b(Context context)
{
    if (a != null)
    {
        break MISSING_BLOCK_LABEL_56;
    }
    a = (ConnectivityManager)context.getSystemService("connectivity");
    try
    {
        b = android/net/ConnectivityManager.getMethod("setMobileDataEnabled", new Class[] {
            Boolean.TYPE
        });
    }
    // Misplaced declaration of an exception variable
    catch (Context context) { }
    c = android/net/ConnectivityManager.getMethod("getMobileDataEnabled", new Class[0]);
    return;
    context;
}

Можно найти ее расширенную версию на Stack Overflow. Там же автор пишет, что она будет работать на всех версиях Android, а с версии 5.0 — только если приложение системное. По моим наблюдениям, так можно включать интернет до версии 4.3.

В основе этого метода лежит рефлексия, через нее вызывают непубличный метод. С точки зрения безопасности это большая проблема, так как любое приложение может включать мобильные данные и выходить в интернет. Поэтому в последних версиях Android эту лазейку прикрыли.

 

Об использовании скрытых методов

Какими бы широкими ни были возможности платформы Android, рано или поздно мы упираемся в ее границы. Вызов скрытых методов при помощи рефлексии позволяет нам эти границы расширить и сделать чуть больше, чем описано в документации. Вот, например, способ соединения Bluetooth-устройств.

Чаще всего границы платформы продиктованы безопасностью. Нельзя же позволять приложению с котиками подключаться к интернету без ведома пользователя или создавать Bluetooth-сети из устройств? В этом случае в угоду удобству нельзя жертвовать безопасностью. Поэтому пусть, чтобы получить новую порцию котиков, пользователь вручную подключает интернет каждый раз. В серьезных проектах скрытые методы использовать не будут, и крупные разработчики их не рекомендуют. В этом я полностью разделяю их мнение: не факт, что то, что работает сейчас, будет работать завтра. А в мире Android-разработки и без этого хватает головной боли...

 

Онлайн-декомпиляторы

Не может не радовать, что с каждым днем у нас все больше инструментов для разбора приложений.
Вот список уже трех подобных сайтов:

Оставить мнение