Показаны сообщения с ярлыком Android. Показать все сообщения
Показаны сообщения с ярлыком Android. Показать все сообщения

пятница, 2 августа 2019 г.

Осознание стандартных функций Kotlin: run, with, let, also и apply

Некоторые стандартные функции Kotlin настолько похожи, что мы не уверены, какую из них использовать. Здесь я представлю простой способ, чтобы четко их различать и выбирать, какую использовать.

Функции скоупа

Функции, на которых я сконцентрируюсь, это run, with, T.run, T.let, T.also и T.apply. Я называю их функциями скоупа (видимости), поскольку я вижу их основную функциональность в том, что они поставляют внутреннюю область для вызывающей стороны.

Самый простой способ проиллюстрировать определение скоупа, это функция run

fun test() {
    var mood = "I am sad"

    run {
        val mood = "I am happy"
        println(mood) // I am happy
    }
    println(mood)  // I am sad
}

Logcat:
I/System.out: I am happy
I/System.out: I am sad

Благодаря этому, внутри функции test вы можете получить отдельный изолированный скоуп, в котором mood переопределен, чтобы напечатать "I am happy".

Эта функция скоупа сама по себе кажется не очень полезной. Но есть и еще один приятный момент, помимо скоупа; он возвращает что-то, то есть последний объект в области видимости.

Следовательно, мы можем "красиво" применить show() к одному из view, не вызывая его дважды:

    run {
        if (firstTimeView) introView else normalView
    }.show()

3 атрибута скоуп функции

Чтобы сделать функции обзора более интересными, позвольте мне классифицировать их поведение по 3 атрибутам. Я буду использовать эти атрибуты, чтобы отличать их друг от друга.

1. Нормальная функция VS функции расширения

Если мы посмотрим на with и T.run, обе функции на самом деле очень похожи. Ниже делается одно и тоже.

with(webview.settings) {
    javaScriptEnabled = true
    databaseEnabled = true
}

// тоже самое что и
webview.settings.run {
    javaScriptEnabled = true
    databaseEnabled = true
}

Их отличает лишь то, что одна из них - нормальная функция with, тогда как другая - функция расширения T.run

В чем же преимущество каждого?

Представьте, что webview.settings может принимать значение NULL, тогда эти две функции выглядеть так:

// Ужас!
with(webview.settings) {
      this?.javaScriptEnabled = true
      this?.databaseEnabled = true
}

// Норм
webview.settings?.run {
    javaScriptEnabled = true
    databaseEnabled = true

}

В этом случае очевидно, что функция расширения T.run лучше, поскольку мы могли бы применить проверку на null перед ее использованием.


2. this VS it

Если мы посмотрим на T.run и T.let, обе функции похожи, за исключением способа, которым они принимают аргумент. Ниже показана одинаковая логика для обеих функций.

stringVariable?.run {
      println("The length of this String is $length")
}

// тоже самое что и
stringVariable?.let {
      println("The length of this String is ${it.length}")

}

Если вы проверите сигнатуру функции T.run, вы заметите, что T.run сделан просто как функция расширения, вызывающая блок T.().
Следовательно, все в пределах скоупа, T может быть обозначено как this.
В программировании this может быть пропущено в большинстве случаев. Поэтому в нашем примере выше мы могли бы использовать $length в выражении println вместо ${this.length}. Я называю это отправкой this в качестве аргумента.

Однако для сигнатуры функции T.let вы заметите, что T.let отправляет себя в функцию, т.е. в блок: (T). Следовательно, это похоже на лямбда-аргумент. Это можно отнести к функции скоупа как есть. Поэтому я называю это отправкой в качестве аргумента.

Из вышесказанного кажется, что T.run лучше, чем T.let, так как он неявен, но есть некоторые тонкие преимущества функции T.let, как показано ниже:
  • T.let обеспечивает более четкое разграничение использования данной переменной функции/члена по сравнению с функцией/членом внешнего класса
  • Если this нельзя пропустить, например, когда оно отправляется в качестве параметра функции, ее запись короче и понятнее
  • T.let позволяет лучше именовать преобразованную используемую переменную, то есть вы можете преобразовать ее в другое имя.


stringVariable?.let {

      nonNullString ->

          println("The non null string is $nonNullString")

}


3. Возвращение this VS другие типы

Теперь давайте посмотрим на T.let и T.also, оба они идентичны, если мы посмотрим на их внутренний скоуп.

stringVariable?.let {
      println("The length of this String is ${it.length}")
}

// Точно тоже самое что и

stringVariable?.also {
      println("The length of this String is ${it.length}")
}

Однако их главное отличие - вот что они возвращают. T.let возвращает значение другого типа, в то время как T.also возвращает сам T, то есть this.

Оба полезны для цепочки функций, где помощь T.let позволяет вам расширить операцию, а T.also позволяет вам использовать ту же самую переменную (т.е. this) в следующем обработчике.

Простая иллюстрация ниже

val original = "abc"

// Обрабатываем значение и отправляем в следующий обработчик
original.let {
    println("The original String is $it") // "abc"
    it.reversed() // evolve it as parameter to send to next let
}.let {
    println("The reverse String is $it") // "cba"
    it.length  // can be evolve to other type
}.let {
    println("The length of the String is $it") // 3
}

// Неправильно
// Тоже самое значение в следующий обработчик (печатает неправильный ответ)
original.also {
    println("The original String is $it") // "abc"
    it.reversed() // even if we evolve it, it is useless
}.also {
    println("The reverse String is ${it}") // "abc"
    it.length  // even if we evolve it, it is useless
}.also {
    println("The length of the String is ${it}") // "abc"
}

// Исправленный вариант also (т.е. манипуляции с исходной строкой)
// Тоже самое значение отправляется в следующий обработчик 
original.also {
    println("The original String is $it") // "abc"
}.also {
    println("The reverse String is ${it.reversed()}") // "cba"
}.also {
    println("The length of the String is ${it.length}") // 3

}

T.also может показаться бессмысленным выше, так как мы могли бы легко объединить их в единый блок функций. Но если хорошо продумать, у него есть несколько хороших преимуществ:
  1. Можно очень четко разделить процесс обработки объекта на маленькие функциональные участки
  2. Подход очень мощный для манипуляций со значениями перед использованием, мы создаем цепочку Builder
Когда два действия объединяются в цепь, т.е. сначала вычисляется одно значение, другое действие ждет, это становится очень мощным подходом, например, ниже:


// Нормальный подход
fun makeDir(path: String): File  {
    val result = File(path)
    result.mkdirs()
    return result
}

// Улучшенный подход
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }


Обзор всех свойств

Посмотрев на 3 свойства, мы в значительной степени знать поведение функции. Позвольте мне проиллюстрировать функцию T.apply, так как она не упоминалась выше. 3 атрибута T.apply, как показано ниже:

1. функция расширения
2. посылает this как аргумент
3 возвращает this (то есть само себя)

Следовательно, можно представить, что его можно использовать следующим образом:

// Нормальный подход
fun createInstance(args: Bundle) : MyFragment {
    val fragment = MyFragment()
    fragment.arguments = args
    return fragment
}

// Улучшенный подход
fun createInstance(args: Bundle) 
              = MyFragment().apply { arguments = args }

Или мы могли бы также сделать цепочку создания объектов:

// Normal approach
fun createIntent(intentData: String, intentAction: String): Intent {
    val intent = Intent()
    intent.action = intentAction
    intent.data=Uri.parse(intentData)
    return intent
}

// Improved approach, chaining
fun createIntent(intentData: String, intentAction: String) =
        Intent().apply { action = intentAction }
                .apply { data = Uri.parse(intentData) }

Выбор функций

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



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





четверг, 25 июля 2019 г.

Kotlin: Деструктуризация

Если вы Android разработчик, которые переходит с Java на Kotlin, одной из наиболее интересных функций, с которой придется столкнуться, является деструктуризация.

Деструктуризация позволяет разбить объект на несколько переменных. Например, у вас может быть класс Person, который состоит из имени и поля возраста:

data class Person (val name: String, val age: Int)

Деструктуризация экземпляров этого класса позволяет эффективно инициализировать переменные внутренними полями объекта Person, как показано ниже:

(1) val person = Person("Thomas", 40)(2) val (name, age) = person // деструктуризация(3) println("$name is $age years old")



>> Tomas is 40 years old


Деструктуризация здесь происходит во второй строке. За сценой происходит следующее:

val name = person.component1()
val age = person.component2()


Пример с Person это типичный пример, который вы можете найти в интернете. Выглядит красиво, но не совсем понятно как это можно использовать на практике.

Для ответа на этот вопрос посмотрим на функционал типичного приложения, допустим нам необходимо иметь дело с данными о местоположении.
Допустим, нам необходимо обрабатывать коллекцию истории объекта Location. Мы по возможности упростим пример, наш Project Manager просто попросил чтобы мы писали широту и долготу каждой позиции в лог.


Наш первый кусок кода может выглядеть примерно так:

fun processLocations(locations: List) {
    for (location in locations) {
        Log.d("TAG", "Lat: ${location.latitude}, Lon: ${location.longitude}")
    }
}


Ну или что-то вроде этого:
fun processLocations(locations: List) { 
    locations.forEach { 
        location -> Log.d("TAG", "Lat: ${location.latitude}, Lon: ${location.longitude}") 
    }
}

Однако, благодаря деструктуризации и расширений языка Kotlin, мы можем записать это кратко, устранив необходимость явного указания имени объекта местоположения. Давайте взглянем.

Сначала мы создадим методы расширения для существующего класса Android Location, чтобы мы могли использовать деструктуризацию:

operator fun Location.component1() = latitude
operator fun Location.component2() = longitude

Теперь, с этими доработками, мы можем изменить наш пример чтобы использовать деструктуризацию. Вот один из примеров:

fun processLocations(locations: List) {
    for ((lat, lon) in locations) {
        Log.d("TAG", "Lat: $lat, Lon: $lon")
    }
}

Или даже еще более краткий:

fun processLocations(locations: List) {
    locations.forEach { (lat, lon) -> println("Lat: $lat, Lon: $lon") }
}

Надеюсь, что эта короткая статья поможет доказать, насколько полезной и откровенно говоря, насколько крутой может быть деструктуризация в Kotlin. 

















среда, 12 марта 2014 г.

Little Fluffy Location Library

Задался поиском энергосберегающей технологии фонового определения координат из сервиса и нашел небольшую библиотечку.
Вебсайт автора http://www.littlefluffytoys.mobi/

Энергосберегающее определение местоположения для Android, использующая при возможности Google Play Location.

Описание

Поместите эту небольшую библиотеку в ваше Android приложение, поправьте манифест, инициализируйте её в методе onCreate ... и пользуйтесь!


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

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

Базируется на принципах и использует адаптированный код из android-protips-location by Reto Meier, из его поста A Deep Dive Into Location.

Требуется Android 2.1 и выше. Лучше всего работает с 2.2 и выше.

Теперь, по возможности, работает с закрытым исходным кодом и авторскими правами Google Play Services (GMS) объединенного провайдера местонахождения. Если это не так (например, на Kindle Fire, Nokia X) используется Location API Android Open Source Project.

Библиотека работает с помощью пассивного Location Listener (возможно только с Android 2.2 и выше, именно поэтому она лучше всего работает с ним), который слушает обновления местоположения по запросу других приложений на вашем телефоне.

Наиболее точные данные о местоположении выполняются запросами для вашего приложения примерно каждые 15 минут. Если обновления данных о местоположении еще не были получены из другого приложения в течение часа, библиотека форсирует обновление данных своими средствами.

Особенности:

  • обновление местоположения примерно каждые 15 минут (если новые обновления данных о местоположении найдены)
  • если нет обновления данных о местоположении в течении часа, обновление происходит принудительно
  • по необходимости, при каждом обновлении данных о местоположении выполняется вещание
  • конфигурируемые частота регулярных трансляций (по умолчанию 15 минут) и принудительного обновления (по умолчанию - 60 минут)
  • принудительная функция обновления
  • возможность получить последнее известное положение
  • выбор наиболее точного провайдера местоположения из возможных
  • абстрагирует сложности Google Play Services по сравнению с Android Open Source
  • необязательный debug output

С чего начать

Для использования библиотеки, добавьте в ваш проект файл littlefluffylocationlibrary.jar как external JAR. В Eclipse, можно просто создать в проекте папку libs и положить файл туда, обновить проект, правым кликом на файле .jar выбрать в меню "Add to build path".
Далее, отредактируйте ваш файл manifest, добавив в него следующее:


 ...  
 <manifest {...}>  
 ...  
  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />  
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />  
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />  
  <uses-feature android:name="android.hardware.location" android:required="true" />  
  <uses-feature android:name="android.hardware.location.gps" android:required="false" />  
  ...  
  <application {...}>  
   ...  
   <service android:name="com.littlefluffytoys.littlefluffylocationlibrary.LocationBroadcastService" />  
   <receiver android:name="com.littlefluffytoys.littlefluffylocationlibrary.StartupBroadcastReceiver" android:exported="true">  
    <intent-filter>  
     <action android:name="android.intent.action.BOOT_COMPLETED" />   
    </intent-filter>   
   </receiver>  
   <receiver android:name="com.littlefluffytoys.littlefluffylocationlibrary.PassiveLocationChangedReceiver" android:exported="true" />  
   ...  
   <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>  
  </application>  
  ...  
 </manifest>  

Внутри метода OnCreate() поместите вызов
 LocationLibrary.initialiseLibrary (getBaseContext (), "com.your.package.name");   


заменив имя пакета на ваш. Здесь также нужно обрабатывать исключение UnsupportedOperationException который указывает, что устройство не имеет провайдеров местоположения.

Теперь мы готовы использовать библиотеку!


Приложение транслирует действие com.your.package.name.littlefluffylocationlibrary.LOCATION_CHANGED с сериализованным объектом типа LocationInfo от com.littlefluffytoys.littlefluffylocationlibrary.LocationInfo которые содержат параметры местоположения. Т.е. вы должны настроить broadcast receiver, который будет слушать это действие. Такая настройка выходит за рамки этой документации, но вы можете скачать пример приложения и посмотреть там как это сделать.

Дополнительная информация

Режим отладки


Отладка по умолчанию отключена, чтобы ее включить, добавьте при инициализации:
 LocationLibrary.showDebugOutput();  

LocationLibraryConstants

Здесь содержится несколько полезных констант и функций:
 public static final long DEFAULT_ALARM_FREQUENCY = AlarmManager.INTERVAL_FIFTEEN_MINUTES;  
 public static final int DEFAULT_MAXIMUM_LOCATION_AGE = (int) AlarmManager.INTERVAL_HOUR;  
 public static final String LOCATION_BROADCAST_EXTRA_LOCATIONINFO = "com.littlefluffytoys.littlefluffylocationlibrary.LocationInfo";  
 public static String getLocationChangedPeriodicBroadcastAction()...  
 public static String getLocationChangedTickerBroadcastAction()...  


Перегруженные методы initialiseLibrary

Вы можете использовать любой из перегруженных методов initialiseLibrary, если вам требуется тонкая настройка параметров. Например, во время тестирования, вы можете установить alarmFrequency 60000 (1 минута в миллисекундах ) и locationMaximumAge в 120000 ( 2 минуты в миллисекундах).

Следующие параметры доступны через различные методы:

alarmFrequency: Как часто транслировать обновление местоположения в миллисекундах, если оно было получено. Для энергосбережения батареи, это должно быть одним из доступных неточных интервалов повторяемости, распознаваемых AlarmManager.setInexactRepeating(int, long, long, PendingIntent). Вам не запрещается использовать любое другого значение, но в этом случае AlarmManager  Android, использует setRepeating вместо setInexactRepeating, и это приводит к ухудшению работы от батареи. По умолчанию используется AlarmManager.INTERVAL_FIFTEEN_MINUTES.

locationMaximumAge: Максимальный таймаут обновления местоположения. Если при срабатывании события местоположение старше этого возраста, будет произведено обновление местоположения. По умолчанию используется AlarmManager.INTERVAL_HOUR.

broadcastEveryLocationUpdate: Если параметр TRUE, в дополнение к трансляции периодических обновлений, передается информация о всех обновлениях местоположения, как он будет найден, используя интент com.your.package.name.littlefluffylocationlibrary.LOCATION_CHANGED_TICK. По умолчанию FALSE.

broadcastPrefix: Настоятельно рекомендуется обеспечить уникальное значение для этого параметра, например по имени вашего пакета. В более ранней версии Little Fluffy Location Library, broadcast, чтобы показать изменение местоположения всегда был тем же - в частности, com.littlefluffytoys.littlefluffylocationlibrary.LOCATION_CHANGED (и LOCATION_CHANGED_TICK , если broadcastEveryLocationUpdate TRUE ).  
 Это означает, что если несколько приложений с использованием библиотеки были установлены на устройстве, каждое приложение будет принимать broadcast, посланный любому из приложений. На момент написания статьи, очень популярное приложение Groupon использует библиотеку и отправляет эту постоянную трансляцию, так что это важный вопрос. Таким образом, для того, чтобы ваше приложение реагировало только на broadcast из вашего приложения, а не дублировало передачи из других приложений, пожалуйста, сделайте уникальное значение для этого параметра. Однако, если вы должны поддерживать обратную совместимость с предыдущей версией этой библиотеки , и вы по какой-то причине не в состоянии переработать свое приложение , просто используйте com.littlefluffytoys для этого параметра.

Доступ к последнему местоположению

Чтобы получить последнюю информацию о местоположении, не дожидаясь трансляции, просто создайте объект LocationInfo: 
 
 LocationInfo latestInfo = new LocationInfo(getBaseContext());  

Если у вас уже есть объект LocationInfo, просто вызовите метод refresh().

Форсирование получения местоположения

Для принудительного обновления местоположения, вызовите LocationLibrary.forceLocationUpdate(), и в ближайшее время вы получите трансляцию, содержащую последнее местоположение.

понедельник, 10 декабря 2012 г.

Android. Лицензирование собственного приложения

Итак, в Google Play у вас есть крутое приложение, которое купит у вас целая куча народа. И вы хотите встроить проверку лицензии на него. Кстати, если вы разместили в маркете бесплатное приложение, платным его сделать уже не получится.

1. Настраиваем консоль разработчика


Наше лицензируемое приложение уже должно лежать в Google Developer Console. С новой политикой Google каждое приложение имеет свой уникальный ключ. Щелкаем по приложению, выбираем в левом меню "Службы и API" и копируем RSA ключ:


Необходимо подготовить аккаунт к тестированию, для этого заходим через левое меню в Настройки - Сведения об аккаунте, добавляем свой e-mail в Аккаунты GMail разработчиков (соответственно этот аккаунт должен у нас быть забит в настройках Вашего устройства). Выбирая в ComboBox "Информация о лицензии" требуемый ответ, можно протестировать реакцию приложения. А вот и картинка этого окна:



2. Настраиваем Eclipse


В Eclipse заходим в Window - Android SDK Manager, секция Extras и скачиваем Google Play Licensing Library (на картинке - в красной рамке):

Теперь подключаем библиотеку к нашему проекту. Для этого жмем правой кнопкой в Package Explorer, далее Import, далее Android - Existing Android Code Into Workspace, находим нашу библиотеку в директории <путь до android sdk>/google-market_licensing. Импортируем.

3. Создаем проект Eclipse


В нашем проекте заходим в свойства проекта, секция Android, нижняя вкладка Library. Подключаем библиотеку к проекту:





Вроде всё готово.

В нашем приложении в Android Manifest.xml в корень manifest добавляем всего одну строчку:
    



Заодно исправим главный layout в res/layout/. Вот его полный код:

В главной активити приложения добавляем импорты:

import com.google.android.vending.licensing.AESObfuscator;
import com.google.android.vending.licensing.LicenseChecker;
import com.google.android.vending.licensing.LicenseCheckerCallback;
import com.google.android.vending.licensing.ServerManagedPolicy;

И следующие члены класса:

    // Поставьте сюда свой RSA ключ из Developer Console
    private static final String BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFveIcGxnHJQfSUs6S5eoWwYr87LfNBupk9G0rN1QZ3YiVYzp/LeJ7fNkDjC1f8RHMeJF3ZA9VjLH5d4TUmQ3M4e6/vVrNFga+BXEbAmhsv6aQ1fNzt5tBWFYUdGlhHTcfsTiFPDh17ejlfm7XlhxWuYNLuxJtzXpwdiqTqiTZed0mFut1Z1khL+34SXL4qDzegbkSdxrka/zyLnuS5dSyacszmyST7x+/NjWgg/9zlu+FRETXl+XYO2STb6RuVVgLaQIDAQAB";

    // Забейте сюда какие-нибудь числа. Свои, иначе ничего не заработает!
    private static final byte[] SALT = new byte[] { -46, 65, 30, -19, -103, -5, 74, -64, 51, 88, -95, -45, 77, -117, -36, -113, -11, 32, -64, 89 };   
        
        // компоненты на лэйауте
    private TextView mStatusText;
    private Button mCheckLicenseButton;
        
        // чекер и коллбэк для проверки лицензии
    private LicenseCheckerCallback mLicenseCheckerCallback;
    private LicenseChecker mChecker; 
 
    // Хэндлер для процесса UI.
    private Handler mHandler;
 
Метод onCreate с комментариями:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.main); 
        // получаем компоненты layouta, по кнопке запускаем повторную проверку лицензии 
        mStatusText = (TextView) findViewById(R.id.status_text);
        mCheckLicenseButton = (Button) findViewById(R.id.check_license_button);
        mCheckLicenseButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                doCheck();
            }
        });

        mHandler = new Handler();

        // Попробуйте использовать здесь другие данные для ID устройства. ANDROID_ID будет взламываться в первую очередь.
        String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);

        // Коллбэк функция по окончании проверки лицензии
        mLicenseCheckerCallback = new MyLicenseCheckerCallback();
        // конструктор LicenseChecker с правами.
        mChecker = new LicenseChecker(
            this, new ServerManagedPolicy(this,
                new AESObfuscator(SALT, getPackageName(), deviceId)),
            BASE64_PUBLIC_KEY); 
  
        // запускаем проверку  
        doCheck();
    }
  
    // в методе doCheck() готовим лэйаут к отсылке запроса  
    private void doCheck() {
        mCheckLicenseButton.setEnabled(false);
        setProgressBarIndeterminateVisibility(true);
        mStatusText.setText(R.string.checking_license);
        mChecker.checkAccess(mLicenseCheckerCallback);
    } 

Описываем класс MyLicenseChecker, который служит для обработки ответов от сервера:

 
    private class MyLicenseCheckerCallback implements LicenseCheckerCallback {

     @Override
  public void allow(int reason) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            // Should allow user access.
            displayResult("Allow");
        }

  @Override
  public void dontAllow(int reason) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            displayResult("DONT ALLOW");
            // Should not allow access. In most cases, the app should assume
            // the user has access unless it encounters this. If it does,
            // the app should inform the user of their unlicensed ways
            // and then either shut down the app or limit the user to a
            // restricted set of features.
            // In this example, we show a dialog that takes the user to Market.
            showDialog(0);
        }
  
  @Override
        public void applicationError(int errorCode) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            // This is a polite way of saying the developer made a mistake
            // while setting up or calling the license checker library.
            // Please examine the error code and fix the error.
            String result = String.format("ERROR", errorCode);
            displayResult(result);
        }

    } 

А также несколько функций чтобы просто показать диалог:

    protected Dialog onCreateDialog(int id) {
        // We have only one dialog.
        return new AlertDialog.Builder(this)
            .setTitle("UNLICENSED")
            .setMessage("UNLICENSED DIALOG BODY")
            .setPositiveButton("BUY BUTTON", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(
                        "http://market.android.com/details?id=" + getPackageName()));
                    startActivity(marketIntent);
                }
            })
            .setNegativeButton("QUIT BUTTON", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    finish();
                }
            })
            .create();
    }    
    
    private void displayResult(final String result) {
        mHandler.post(new Runnable() {
            public void run() {
                mStatusText.setText(result);
                setProgressBarIndeterminateVisibility(false);
                mCheckLicenseButton.setEnabled(true);
            }
        });
    }  


Собственно и всё. Запуская приложение и изменяя требуемый ответ через Developer Console(Информация о лицензии), получаем требуемые данные
P.S.: Если в консоли видим, что типа Using cached license response, меняйте SALT.

Полный код активити:

package com.osmsoft.licensingexample;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings.Secure;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;

import com.android.vending.licensing.AESObfuscator;
import com.android.vending.licensing.LicenseChecker;
import com.android.vending.licensing.LicenseCheckerCallback;
import com.android.vending.licensing.ServerManagedPolicy;

public class MainActivity extends Activity {
    private static final String BASE64_PUBLIC_KEY = "REPLACE THIS WITH YOUR PUBLIC KEY";

    // Generate your own 20 random bytes, and put them here.
    private static final byte[] SALT = new byte[] {
        -46, 65, 30, -128, -103, -57, 74, -64, 51, 88, -95, -45, 77, -117, -36, -113, -11, 32, -64,
        89
    };

    private TextView mStatusText;
    private Button mCheckLicenseButton;

    private LicenseCheckerCallback mLicenseCheckerCallback;
    private LicenseChecker mChecker;
    // A handler on the UI thread.
    private Handler mHandler;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.main);

        mStatusText = (TextView) findViewById(R.id.status_text);
        mCheckLicenseButton = (Button) findViewById(R.id.check_license_button);
        mCheckLicenseButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                doCheck();
            }
        });

        mHandler = new Handler();

        // Try to use more data here. ANDROID_ID is a single point of attack.
        String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);

        // Library calls this when it's done.
        mLicenseCheckerCallback = new MyLicenseCheckerCallback();
        // Construct the LicenseChecker with a policy.
        mChecker = new LicenseChecker(
            this, new ServerManagedPolicy(this,
                new AESObfuscator(SALT, getPackageName(), deviceId)),
            BASE64_PUBLIC_KEY);
        doCheck();
    }

    protected Dialog onCreateDialog(int id) {
        // We have only one dialog.
        return new AlertDialog.Builder(this)
            .setTitle("UNLICENSED")
            .setMessage("UNLICENSED DIALOG BODY")
            .setPositiveButton("BUY BUTTON", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(
                        "http://market.android.com/details?id=" + getPackageName()));
                    startActivity(marketIntent);
                }
            })
            .setNegativeButton("QUIT BUTTON", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    finish();
                }
            })
            .create();
    }   

    private void doCheck() {
        mCheckLicenseButton.setEnabled(false);
        setProgressBarIndeterminateVisibility(true);
        mStatusText.setText(R.string.checking_license);
        mChecker.checkAccess(mLicenseCheckerCallback);
    }

    private void displayResult(final String result) {
        mHandler.post(new Runnable() {
            public void run() {
                mStatusText.setText(result);
                setProgressBarIndeterminateVisibility(false);
                mCheckLicenseButton.setEnabled(true);
            }
        });
    }

    private class MyLicenseCheckerCallback implements LicenseCheckerCallback {

     @Override
  public void allow(int reason) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            // Should allow user access.
            displayResult("Allow");
        }

  @Override
  public void dontAllow(int reason) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            displayResult("DONT ALLOW");
            // Should not allow access. In most cases, the app should assume
            // the user has access unless it encounters this. If it does,
            // the app should inform the user of their unlicensed ways
            // and then either shut down the app or limit the user to a
            // restricted set of features.
            // In this example, we show a dialog that takes the user to Market.
            showDialog(0);
        }
  
  @Override
        public void applicationError(int errorCode) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            // This is a polite way of saying the developer made a mistake
            // while setting up or calling the license checker library.
            // Please examine the error code and fix the error.
            String result = String.format("ERROR", errorCode);
            displayResult(result);
        }

    }  

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mChecker.onDestroy();
    }

}