Pull to refresh

Как же работает этот загадочный HTC Dot View?

Reading time 10 min
Views 53K
В мире HTC существует такая штука, как Dot View. HTC Dot View — это весьма оригинальный чехол, который позволяет пользователям передовых моделей от HTC(линейки One) использовать свой девайс весьма нестандартным образом. Данный аксессуар был призван принести еще большую славу компании, и, кажется, со своей задачей он успешно справляется: многие люди делают свой выбор в пользу HTC One * и благодаря чехлу Dot View.

Подобные аксессуары привлекают не только пользователей делать их многочисленные обзоры, но и мобильных разработчиков создавать нестандартные приложения, которые задействуют эти прикольные фичи. Стандартный набор возможностей Dot View от производителя(HTC) довольно богат: удобный плеер с промоткой треков, вывод всевозможных уведомлений «на точки» чехла и даже простые игры, напоминающие своими «большими пикселями» ушедшую «восьмибитную эпоху». Но проблема вот только в том, что официальный разработчик не предоставил не только API для использования, но даже ни малейшей инструкции на тему того, как же работает его детище Dot View. Это привело к тому, что энтузиастам пришлось разбираться самостоятельно в механизмах сопряжения чехла с девайсом, а исследователям — исследовать.

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





Все это — один большой заговор !?



Как уже и говорилось выше, данный чехол был выпущен официальным производителем специально для своих устройств.
Чехол устроен таким образом, что магнит, вмонтированный в него, взаимодействует с внутренней аппаратурой, позволяя ей понять, что на телефон надет чехол, и этот чехол — Dot View. А сам магнит расположен здесь, прямо под маркировкой HTC:



После надевания чехла на устройство, запущенные в системе сервисы начинают взаимодействовать с чехлом, перехватывая режимы управления энергопотребления(состояния) устройства и запуская «специфичекие» активности от официального производителя. Т.е мобильные устройства сопряжены с данным чехлом как на программном уровне, так и на аппаратном. Чуть более детальный обзор можно посмотреть вот тут.

Что делать, если API не существует?



Именно таким вопросом приходилось задаваться поначалу… Действительно, было сложно поверить, что для такого уникального аксессуара не было выпущено никакого API для сторонних разработчиков. В процессе поиска в сети попадались некоторые сорцы, так или иначе относящиеся к делу, вроде реализации модуля Dot View для кастомного фреймворка Xposed, но ничего такого, от чего можно было быпо настоящему оттолкнуться и, наконец, задействовать возможности Dot View в собственных приложениях. Поэтому пришлось начинать собственное расследование.

Первым шагом на пути к пониманию истины был просмотр logcat, в котором были сорцы, точно указывающие на присутствие в системе некоего головного компонента Dot View:

W/SensorService(  870): Listener[0] = com.htc.dotmatrix.CoverService$7@24541961
W/SensorService(  870): Listener[1] = com.htc.dotmatrix.CoverService$7@6b8ab5b
W/SensorService(  870): Listener[2] = com.htc.dotmatrix.CoverService$7@f1d0b5a
...
D/DotMatrix( 1287): [CoverService] resetCoverViewState, poleN: false, state: true
D/DotMatrix( 1287): [CoverService] updateCoverState, (sticky)poleN: false, state: true
D/DotMatrix( 1287): [CoverService] updateCoverState, (intent)poleN: false, state: false
D/DotMatrix( 1287): [CoverService] updateCoverState, removeMessages EVENT_COVER_CLOSED
D/DotMatrix( 1287): [CoverService] show cover
D/DotMatrix( 1287): [DotImage] init com.htc.dotmatrix.ui.BackgroundImage
D/DotMatrix( 1287): [DotImage] init com.htc.dotmatrix.ui.TemperatureImage
D/DotMatrix( 1287): [DotImage] init com.htc.dotmatrix.ui.LastCallImage


Первым делом в голову пришла идея о декомпиляции APK официального приложения HTC Dot View, ведь самые интересные логи из logcat`а ссылались именно на имя пакета данного приложения. Но в итоге никакой полезной информации, позволяющей написать свой код для чехла, извлечь так и не удалось за исключением специфичных интент-экшенов, используемых широковещательными приемниками и предназначенных для отслеживания состояния чехла-обложки: открыта или же закрыта.

Спустя некоторое время безуспешных попыток достучаться до сути, случайно удалось наткнуться на раздел «Приложения» в официальном приложении от HTC для Dot View, в котором было несколько приложений «по умолчанию»:






Тут стало интересно, чем вообще обычное приложение для Android отличается от приложения для Dot View. Т.е на каком уровне происходит разделение? Поискав в Google Play другие приложения, удалось выйти на реализацию Dot Breaker, представляющее из себя игру в «выбивание кирпичиков». Конечно же, как и все остальные, оно было от официального производителя.

Именно благодаря приложению Dot Breaker и удалось выяснить все тонкости работы Dot View и создать собственное приложение для чехла.

Поиски сути...



Произведя первичную декомпиляцию приложения, появилась возможность заглянуть в манифест приложения(AndroidManifest.xml).
Данный манифест был мало чем похож на манифест официального приложения HTC Dot View. Самое первое, что бросилось в глаза, так это отсутствие «простыни» разрешений:

<uses-permission android:name="android.permission.GET_TASKS"/>
<uses-permission android:name="com.htc.permission.dotviewgame"/>


Ведь приложение, как минимум, управляло состоянием устройства. А разрешение GET_TASKS вообще признано устаревшим. Поэтому следующее разрешение и представляло наибольший интерес. Из его имени очевидно, что оно нестандартное.

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

...
<activity  android:name=".WelcomePageActivity" android:screenOrientation="portrait" >
    <intent-filter>
        <action android:name="com.htc.intent.action.dotviewgame_launch"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>
...


Грубо говоря, этот кастомный action — это как «android.intent.category.LAUNCHER», только «com.htc.intent.action.dotviewgame_launch».
А еще в ассетах(assets) была собрана вся анимация, присутствующая в игре:



… и в дальнейшем станет ясно почему, ведь отрисовка «на точках» чехла — занятие не слишком стандартное.
Далее последовала вторичная декомпиляция, целью которой выступал непосредственный анализ декомпилированного Java-кода приложения. (Декомпилированный код представлен в репозитории на BitBucket.)

Исходя из приведенного выше кусочка манифеста, можно сделать вывод о том, что в роли главной активности приложения Dot Breaker выступает активность WelcomePageActivity. Единственное, что она делает, так это показывает начальную анимацию(еще никак не связанную с Dot View) длительностью 2 секунды и запускает активность GameLevel, отвечающую за выбор уровня:

...
this.mCountDownTimer = new CountDownTimer(2000L, 2000L)
{
      public void onFinish()
      {
        WelcomePageActivity.this.finish();
        WelcomePageActivity.this.startActivity(new Intent(WelcomePageActivity.this, GameLevel.class));
      }
} .start();
...


Активность GameLevel же позволяет выбрать уровень. В ее восстановленном Java-коде можно увидеть «простыню» инициализаций всех 20 уровней приложения; при клике на View каждого из них будет всплывать AlertDialog, содержащий вот такой код вызова ключевой активности приложения — GameActivity:

...
localBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener()
{
    public void onClick(DialogInterface paramAnonymousDialogInterface, int paramAnonymousInt)
    {
        GameLevel.this.startActivity(new Intent(GameLevel.this, GameActivity.class));
    }
});
...


Этот код представляет из себя «последний рубеж», за которым, наконец, следуют внутренние механизмы реализации концепции HTC Dot View.

Как же работает этот Dot View?



Ключевая активность GameActivity, которая является промежуточным звеном в цикле взаимодействия с Dot View, ничем особенным не выделяется — она, так же как и любая другая активность, наследуется от класса Activity. Это означает, что взаимодействие начинается с метода onCreate. В коде этого метода активности GameActivity можно обнаружить множество вызовов, отвечающих за конфигурацию и стилизацию окна(фулскрин, яркость и т.д), за которыми следует ключевой вызов в самом конце метода:

...
this.mDotViewCommunity.bindDotViewService();


Здесь this.mDotViewCommunity — экземпляр класса-оболочки, представляющий из себя простую абстракцию и реализованный внутри класса активности(GameActivity) как вложенный класс. На него можно не обращать внимания, так как существенной роли данный класс не играет.
Зато немаловажную роль играет метод с говорящим(даже кричащим) названием «bindDotViewService», содержащийся в этом классе. На данном этапе уже можно предположить, что происходит связывание с неким сервисом, возможно, в целях межпроцессного взаимодействия(IPC).

Вот ключевой код из метода bindDotViewService:

...
DotViewSericeConnection mServiceConnect = new DotViewSericeConnection();
...
Intent localIntent = new Intent();
localIntent.setClassName("com.htc.dotmatrix", "com.htc.dotmatrix.GameService");
bool3 = GameActivity.this.bindService(localIntent, this.mServiceConnect, 1);
...


Как можно видеть, действительно происходит связывание с кастомным сервисом Dot View в целях дальнейшего взаимодействия с ним. Имя класса сервиса — com.htc.dotmatrix.GameService, а пакетом(приложением), которому этот сервис принадлежит, является то самое официальное приложение HTC Dot View. DotViewSericeConnection — это кастомный класс Dot Breaker, который является реализацией стандартного интерфейса Android ServiceConnection для мониторинга состояния сервиса. Реализован класс DotViewSericeConnection также в виде вложенного в класс активности класс.

Что ж, возможно, это ожидаемый исход, но каким же образом разворачивается взаимодействие игры Dot Breaker после связывания с сервисом от HTC?

Интерфейс ServiceConnection требует наличия переопределенного метода onServiceConnected(), который вызывается в случае удачного сопряжения с сервисом.
В случае же удачного соединения с сервисом, вызывается код, исполнение которого является первым из двух шагов на пути к использованию Dot View:

public void onServiceConnected(ComponentName paramComponentName, IBinder paramIBinder)
{
    GameActivity.DotViewCommunity.this.mService = new Messenger(paramIBinder);
    try
    {
        Message localMessage = Message.obtain(null, 1);
        localMessage.replyTo = GameActivity.this.mMessenger;
        Bundle localBundle = new Bundle();

        // передача данных об активности, которая "хочет" использовать Dot View
        localBundle.putString("activityName", GameActivity.class.getName());
        localMessage.setData(localBundle);

        // посылка данных сервису
        GameActivity.this.mService.send(localMessage); 

        return;
    }
    catch (RemoteException localRemoteException){ ... }
}


Здесь можно наблюдать довольно стандартное в контексте Android клиент-серверное «общение» с удаленным сервисом.
Из глобального пула вытаскивается сообщение с what-кодом = 1.
А в начале класса активности можно обнаружить следующий набор констант:

static final int MSG_DOTVIEW_CLIENT_PAUSE = 4;
static final int MSG_DOTVIEW_CLIENT_REGISTER = 1;
static final int MSG_DOTVIEW_CLIENT_RESUME = 3;
static final int MSG_DOTVIEW_CLIENT_UNREGISTER = 2;


Это означает, что код данного метода зарегистрировал активность как нового потенциального пользователя.
Дальше происходит вызов аналогичного кода, но с what-кодом «3» — MSG_DOTVIEW_CLIENT_RESUME.
Сразу после чего, Dot View прекращает реагировать на захлопывание крышки для данной активности и предоставляет отрисовку экрана-Dot-View той активности, которая этого хочет, т.е GameActivity.

Ну, а что же после связки и общения с сервисом?



После сообщения сервису com.htc.dotmatrix.GameService о том, что активность хочет и готова использовать чехол «по-своему», при захлопывании крышки чехла, экран не будет гаснуть(поначалу возникнет ощущение, что функции Dot View попросту перестали работать), просто отображая разметку той активности, которая «захватила» режим Dot View. Если же экран погас, то после двойного тапа(стандартный способ «разбудить» Dot View) вместо заставки, опять же, будет разметка «активности-захватчика».

Активность при этом работает практически стандартно: на нее можно помещать кнопки, и их onclick`и будут срабатывать, можно использовать стандартные детекторы жестов Android — все будет работать совершенно обычно.

Но в чем же тогда сложность?
Как-то в начале было сказано, что Dot Breaker хранит всю свою анимацию в картинках. Одна из основных причин(но едва ли самая главная) — специфичность отрисовки на точках Dot View. Именно поэтому ни одно официальное приложение и не использует стандартных View Android, т.к их просто-напросто не будет видно! Кнопка со своим стандартным шрифтом даже не будет выглядеть как кнопка, скорее, как какое-то View-образное пятно, которое можно нажать и что-то произойдет «под чехлом». Казалось бы, можно увеличить размер кнопки и текста, но, хоть все и станет визуально разборчивее, все же нужен более надежный подход.

Внутри класса BrickView приложения Dot Breaker представлены сорцы(частично логика может быть непонята из-за не совсем корректной декомпиляции) способа конвертации экранных пикселей в DotView-пиксели. С ним определенно стоит ознакомиться. Есть и более простой, но менее точный способ — посчитать количество точек чехла:



В итоге получилось 27 весомых точек по X и 48 по Y. Под «весомой» следует понимать такую DotView-точку, которая налагается на дисплей устройства. Обладая такой информацией несложно написать метод, который бы производил нужное масштабирование.

Завершение: 1.



Весь вышесказанный материал будет действительно интересен в контексте какой-нибудь практической реализации.
В качестве таковой хорошо подошла бы какая-нибудь простенькая игра для Dot View.

Всё тестовое демо приложение инкапсулирует в себе 3 активности:

1) MainActivity — служит только для отображения приложения в меню приложений(поддержка стандартного запуска) и запуска второй активности или выхода из приложения.

2) DotRunner — промежуточная активность, главная цель которой — отслеживание состояния крышки чехла посредством широковещательно приемника и запуск/остановка третьей активности:

...
BroadcastReceiver dotReceiver =  new DotReceiver();
// кастомный(недокументированный) intent-action
IntentFilter intentFilter = new IntentFilter("com.htc.cover.closed");
registerReceiver(dotReceiver,intentFilter);
...
public class DotReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

            boolean isCoverClosed = intent.getBooleanExtra("state",false);

            if(isCoverClosed) { 
                startActivity(new Intent(DotRunner.this, DotActivity.class));           
            }
            else{ ... }

        }
 }


3) DotActivity — активность, взаимодействующая с Dot View по правилам, описанным в предыдущих разделах.

Для более удобной отрисовки экрана Dot View с учетом масштабирования понадобилось создать кастомный view DotMatrixView, в котором и происходит игровой-тестовый-процесс и который реализует всю логику отрисовки «больших пикселей».

Игра, в свою очередь, предельно проста: нужно, тапая по кубику, не дать ему упасть:






А для начала новой игры надо будет открыть и закрыть крышку Dot View. Кроме того, внутри класса игры DotMatrixView был реализован интерфейс распознавания жестов(используя класс GestureDetector), с помощью которого можно будет начать игру заново, осуществив Fling-жест по чехлу Dot View.

Демонстрационное видео работы кастомного приложения можно посмотреть здесь.
Исходный код тестового приложения представлен в репозитории на BitBucket.

Завершение: 0.



Рассмотрев принципы работы HTC Dot View, стало ясно, что никаких секретных принципов данная идея не задействует: все то же старое, но «под новой обложкой».

Без сомнения можно заявить, что современным пользователям очень повезло со временем, в котором они существуют: вокруг так много полезных и интересных приспособлений, что за всеми даже не получается уследить, не то что испытать. А разработчики вещей наподобие Dot View и создают эту современность. Ведь именно из подобных мелочей и формируется целое. Поэтому открытое и доступное API для таких «мелочей» — нечто большее, чем просто программный интерфейс. Это способность по-новому смотреть на использование старых концепций.
«Все новое — это хорошо забытое старое»(с).
Tags:
Hubs:
+18
Comments 10
Comments Comments 10

Articles