Google Play IAB: боль

\"billing-not-supported-on-publisher-account\"
Казалось бы, гугл стремится зарабатывать на продажах приложений и айтемов внутри них, для этого поддерживает магазин в котором распостраняются приложения. Логично предположить, что он делает все, чтоб упросить процесс приёма денег у пользователей и увеличенить конверсии (на самом деле нет, показывает опыт). Это такой понятный клон iTunes для экосистемы Android.

Но не все так просто. Давайте посмотрим мучения разработчика, которому нужно добавить покупки внутри приложений(In-App Purchase) и протестировать их. Приведу только базовый сценарий – в зависимости от приложения и амбиций может быть много разветвлений

Read more

Android: сниппет для тостов

Toast прекрасный способ оповестить пользователя о чем то, не блокируя интерфейс. У них можно завадать отзывы, показывать в разных местах и даже делать кастомные view и все это с помощью fluent interface. Уверен, что читатели если и слышали о таких расширенных возможностях, то никогда не пользовались этим. Самый стандартный юзкейс – показать текст.

По моему опыту часто нужно показать toast как результат не успешной процедуры, которая обычно выполняется в фоне в отдельном треде или воркере, т.е. не в UI. А для этого приходится писать лапшу только для того, Toast.makeText(…).show() выполнился в UI треде.

Для этого набросал сниппет, который уменьшит количество кода и ошибки, связанные с вызовом тоста из нетого потока и банальному забыванию метода show().

Было

Toast.makeText(this,
    getText(R.string.messages_storage_invalid),
    Toast.LENGTH_SHORT).show();

Стало

    toastShort(R.string.messages_storage_invalid);

Причем метод можно вызывать из любого потока. Показыватся тост будет из UI треда через runnable

Read more

Kotlin: джависты, завидуйте

Около года назад, в подкасте радио-т я впервые услышал о инициативе JetBrains, новом языке программирования kotlin. С тех пор внимательно слежу за его развитием.
Они позиционируют котлин как “better java” и, надо сказать, у это получается. Это статический типизированный, язык со вшитой nullable-проверкой Так же он поддерживает функции высшего порядка(замыкания), extension functions и trait. Может немного напоминать scala – но, достаточно далеко от неё.

Read more

Разработка: разница обучения на курсах и самостоятельно

Решили начать карьеру программиста?
С чего начать?
На какие курсы пойти?

Пришлось задуматься, чем отличаются люди, которые посещали обычные оффлайн курсы и те, которые учились самостоятельно

Read more

nodejs: Отправка файла REST-запросом

Отправка файла и полей с данными как REST запрос из nodejs сервера на coffeescript

По началу кажется простой задачей – на stackoverflow активно рекомендуют модуль request, он правда классный. Гнётся во все стороны.. но..

  1. На сайте есть пример как отправить файл, но не бинарный, а текстовый. И почему то если натравить запрос с таким “файлом” опять же ноде(другому серверу) он не видится как файл
  2. Гуглить любые проблемы связанные с модулем крайне затруднительно – автор решил не задумываться и дал незамысловатое название, благодаря чему гуглится все, что угодно, только не то нужно.

Read more

Play Framework: локализация Date.since()

Play framework позволяет в шаблонах groovy использовать extension functions – т.е прицеплять методы на ходу к объектам.

В частности в play имеет расширение экземпляров класса Date – since(), который возвращает сколько времени прошло от указанной даты. Например “13 minutes ago”

С since() все хорошо, пока не требуется интернационализация. Если переопределить соответствующие ключи в файлах перевода, то для русского он возвращает нечто вроде 3 дняs назад. Другими словами этот метод не может отображать количество пройденного времени на любом языке, кроме английского.

Read more

HTC Desire: съел всё место

Внезапно, у меня опять кончилось место в телефоне.

\"\"

Пошёл по известной дорожке:

  • Удалить старые приложения
  • Почистить кэш (твиттера, например)
  • Recorvery -> Wipe dalvik cache

Но в этот раз не помогло. Снёс кучу нечасто используемых приложений: через 30 минут снова стало свободно 0.5Mb.

Место кончилось на столько, что даже телефон не открывался, как и многие приложения.
Стандартная утилита не показывала каких-либо сверх-тяжелого софта (я рассчитывал где-то на 40+мб)

Напомню, что у меня HTC Desire с Runnymede AIO V6.0.4.3, прошивка стоит около 14 месяцев

Read more

Android: не используйте String.isEmpty

Ни eclipse ни intellj Idea ни android lint не предупреждают, что String.isEmpty() появился в Java 6, и после Android 2.3.

java.lang.NoSuchMethodError: java.lang.String.isEmpty
at pro.ezway.carmonitor.entity.TroubleCode.hasTitle(TroubleCode.java:142)
at pro.ezway.carmonitor.ui.fragments.TroubleCodesFragment$TroubleCodesAdapter.getView(TroubleCodesFragment.java:242)
at android.widget.AbsListView.obtainView(AbsListView.java:1315)
at android.widget.ListView.makeAndAddView(ListView.java:1727)

Read more

Android: запуск дефолтного ланчера

Есть неочевидная ситуация: вы пишете лаунчер лончер или что-то его заменяющее, отдебажили, всё работало. Конечно же, вы поставили его как програму по умолчанию и теперь он стартует вместо дефолтого. И вдруг, вылезает некая бага, которая начинает крушить, бомбить ронять телефон, перезапуская приложение. Перезапустится, конечно же, то, что упало, а упал лончер. И он снова падает. Development mode в продакшене отключён. Ну вы поняли.

На самом деле бывают ситуации ещё хуже, например, для vendor lock-in приложений, которые вообще скрывают, что они работают под андроид.

Предвкушая первую мысль как вернуть дефолтный лончер: вызвать в приложении finish(). Чёрт, это же домашний экран %)

Read more

Groovy: java слишком проста

Возьмём довольно частую задачку: есть несколько значений первой переменной, нужно сделать присвоить второй переменной какое-то значение, в зависимости от первой переменной, т.е. value mapping.

В джаве это делается примерно так:

String alertClass = "";
switch(code.toUpperCase().charAt(0)) {
case 'U':
    alertClass = "alert";
    break;
case 'P':
    alertClass = "alert-error";
    break;
case 'B':
    alertClass = "alert-warning";
    break;
case 'C':
    alertClass = "alert-info";
    break;
}
// now alertClass has value

Read more