пятница, мая 06, 2016

How to use custom gradle generated constant in android java code

It is really easy to define gradle-generated constants and use those constants in android java code.
For example, I want to use a build type dependent constant RETRY_COUNT.

Gradle's class BuildType has a method:
    public void buildConfigField(String type, String name, String value)

This method can be used like this:

android {
...

    buildTypes {
        release {
            buildConfigField "int", "RETRY_COUNT", "3"
...
        }

        debug {
            buildConfigField "int", "RETRY_COUNT", "10"
...
        }
    }
}

Next, I rebuild my project, open BuildConfig and see:

public final class BuildConfig {
...
public static final boolean RETRY_COUNT = 10;
}



вторник, марта 15, 2016

Custom view, EditText, проблема при восстановлении состояния (use saveEnabled(fals))

С большим интересом потратил несколько часов на локализацию следующей проблемы:
- у меня есть custom view
- это композитный view, внутри которого используется EditText
- таких view у меня на экране несколько
- custom view обрабатывает сохранение и восстановление состояния в методах onSaveInstanceState и onRestoreInstanceState
- дебаггер показывает, что вызов этих методов и их работа выполняются корректно.

Но: после смены ориентации экрана в каждом из этих view содержится текст, который был введён в самый нижний из этих view.

Решение:
для EditText, входящего в состав композитного view, запретить сохранение состояния:

<EditText
    android:id="@+id/value"
    android:saveEnabled="false"
    .../>


понедельник, февраля 29, 2016

Robolectric, getActivityInfo, NullPointerException at start

I have created a robolectric test, when I started one, I have got an exception:

java.lang.NullPointerException
at org.robolectric.res.builder.DefaultPackageManager.getActivityInfo(DefaultPackageManager.java:173)
at org.robolectric.util.ActivityController.getActivityInfo(ActivityController.java:65)
at org.robolectric.util.ActivityController.attach(ActivityController.java:51)
at org.robolectric.util.ActivityController$1.run(ActivityController.java:121)
at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:304)
at org.robolectric.shadows.CoreShadowsAdapter$2.runPaused(CoreShadowsAdapter.java:45)
at org.robolectric.util.ActivityController.create(ActivityController.java:118)
at org.robolectric.util.ActivityController.create(ActivityController.java:129)
at ru.simpls.brs2.mobbank.ui.modules.freereqpayment.ui.FreeReqPaymentActivityTest.init(FreeReqPaymentActivityTest.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:251)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:188)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:152)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

Process finished with exit code -1

Here is my test's declaration:

@RunWith(RobolectricGradleTestRunner.class)
@Config(
        constants = BuildConfig.class,
        sdk = 21,
        manifest = "src/main/AndroidManifest.xml")
public class FreeReqPaymentActivityTest {...}

In my project I have some modules. I have imported BuildConfig from another module, not from one where test is defined.

So when I have changed the import to a correct module, and problem was solved.

среда, декабря 09, 2015

First launch of Activity with Google Maps is very slow / Существенная задержка при запуске activity с google map

I have a "main" activity - and an activity with mapView. When that activity-with-mapView starts for a first time, it is really slow.

clocksmith's post gave me an idea to start initialization from main activity in a separate thread. And it really solves the problem.

Here is my code from "main" activity:

    public void onCreate(Bundle savedInstanceState) {
        ...

        Runnable initMap = () -> {
            BaseApplication.d("Start init mapView");
            MapView mapView = new MapView(Home.this);
            mapView.onCreate(null);
            BaseApplication.d("... done");
        };
        new Thread(initMap).start();
    }

mapView is never used - it's only for initialization purpose.

And here is a stack trace - just for info:

    12-09 19:31:54.442 17172-17341/my.app D/XXX: Start init mapView
    12-09 19:31:54.525 17172-17341/my.app I/zzy: Making Creator dynamically
    12-09 19:31:55.007 17172-17341/my.app D/ChimeraCfgMgr: Reading stored module config
    12-09 19:31:55.153 17172-17341/my.app D/ChimeraCfgMgr: Loading module com.google.android.gms.maps from APK /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/MapsModule.apk
   
12-09 19:31:55.154 17172-17341/my.app D/ChimeraModuleLdr: Loading module APK /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/MapsModule.apk
    12-09 19:31:55.262 17172-17341/my.app D/ChimeraFileApk: Primary ABI of requesting process is armeabi-v7a
    12-09 19:31:55.271 17172-17341/my.app D/ChimeraFileApk: Classloading successful. Optimized code found.
    12-09 19:31:55.316 17172-17341/my.app W/System: ClassLoader referenced unknown path: /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/native-libs/armeabi-v7a
   
12-09 19:31:55.317 17172-17341/my.app W/System: ClassLoader referenced unknown path: /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/native-libs/armeabi
    12-09 19:31:55.618 17172-17341/my.app I/Google Maps Android API: Google Play services client version: 7571000
    12-09 19:31:55.630 17172-17341/my.app I/Google Maps Android API: Google Play services package version: 8489438
    12-09 19:31:55.969 17172-17341/my.app I/e: Token loaded from file. Expires in: 423267993 ms.
    12-09 19:31:55.969 17172-17341/my.app I/e: Scheduling next attempt in 422967 seconds.
    12-09 19:31:56.338 17172-17341/my.app D/XXX: ... done

As we can see, it realy takes a lot of time...

вторник, декабря 01, 2015

Установить английский язык в Photoshop CC 2015 / How to switch to English in Photoshop 2015

1. Перейти в / go to
C:\Program Files\Adobe\Adobe Photoshop CC 2015

2. Перейти в / go to
Locales\ru_RU\Support Files

3. Переименовать  / rename
tw10428.dat -> tw10428.dat_

4. Перезапустить / restart

четверг, мая 07, 2015

Custom isResumed method crashes application at start with java.lang.LinkageError exception

I have found an interesting case producing the java.lang.LinkageError exception.

In my base activity there is an isResumed field, I set it to true in onResume - and to false in onPause.
It's protected field.

Today I decided to add a method to get that value from a fragment, so I have added isResumed() method

public class BaseActoinBarActivity extends ActionBarActivity {

    protected boolean isResumed;

    @Override
    protected void onResume() {
        super.onResume();
        isResumed = true;
        ...
    }

    ...


    public boolean isResumed() {
        return isResumed;
    }
    
    ...
}

public class StartActivity extends BaseActionBarActivity {
...
}

Ok, after I have started my application I have got an exception:

05-07 18:45:21.020  14466-14466/? I/art﹕ Rejecting re-init on previously-failed class java.lang.Class<ru.xxxxxx.ui.base.BaseActionBarActivity>
05-07 18:45:21.020  14466-14466/? I/art﹕ Rejecting re-init on previously-failed class java.lang.Class<ru.xxxxxx.ui.base.BaseActionBarActivity>
05-07 18:45:21.020  14466-14466/? I/art﹕ Rejecting re-init on previously-failed class java.lang.Class<ru.xxxxxx.ui.StartActivity>
05-07 18:45:21.021  14466-14466/? I/art﹕ Rejecting re-init on previously-failed class java.lang.Class<ru.xxxxxx.ui.StartActivity>
05-07 18:45:21.021  14466-14466/? D/AndroidRuntime﹕ Shutting down VM
05-07 18:45:21.274  14466-14466/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: ru.xxxxxx.ui, PID: 14466
    java.lang.LinkageError: ru.simpls.mtsbank2.ui.StartActivity
            at dalvik.system.DexFile.defineClassNative(Native Method)
            at dalvik.system.DexFile.defineClass(DexFile.java:226)
            at dalvik.system.DexFile.loadClassBinaryName(DexFile.java:219)
            at dalvik.system.DexPathList.findClass(DexPathList.java:321)
            at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:54)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
            at ru.simpls.mtsbank2.ui.SplashScreen$1.handleMessage(SplashScreen.java:42)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5254)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

It's really funny :)

(SplashScreen is a splash screen activity, it starts StartActivity)

вторник, марта 10, 2015

Как обработать pdf в WebView / How to handle pdf in WebView

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

Удобно использовать API от Гугла:

private static final String GOOGLE_DOC_VIEW_URL = "https://docs.google.com/viewer?embedded=true&url=";

...

WebViewClient viewClient = new WebViewClient() {
   ...
    @Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.endsWith("pdf")) {
String googleDocsLink = GOOGLE_DOC_VIEW_URL + url;
Uri uri = Uri.parse(googleDocsLink);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
}

...

webView.setWebViewClient(viewClient);

среда, февраля 11, 2015

AnimatonDrawble - handle animation end / обработать окончание анимации


AnimationDrawble после запуска постоянно возвращает true в результате вызова isRunning, что особенно удивляет в случае onShot = true.

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


After animationDrawbale was started, isRunning method always returns true regardless the onShot value.
On way to handle animation's end is to calculate the total animation's duration and to send an event after that duration is expired.

private AnimationDrawable timerAnimation;

...

private void startTimerAnimation() {
int timerAnimationDuration = calcTimerAnimationDuration();
timerAnimation.start();
timerAnimationEndHandler.sendEmptyMessageDelayed(0, timerAnimationDuration);
}
   

private int calcTimerAnimationDuration() {
    int total = 0;
    for (int i = 0; i < timerAnimation.getNumberOfFrames(); i++) {
        total += timerAnimation.getDuration(i);
    }
    return total;
}


private Handler timerAnimationEndHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
onTimerAnimatonFinished();
};

пятница, июня 06, 2014

MS Office 2013 installation error: 12002-4 / Ошибка 12002-4 при установке Office 2013

I've got this error while installing Office 2013.

How I fixed it:
1. run the FixIt tool to uninstall and check config (http://support.microsoft.com/kb/2739501)
2. defragment the disk (wtf?!!). I don't know why, but it works (I have found this recommendation on MS site).

А теперь по-русски.
Итак, при устновке Office 2013 поймал указанную ошибку. 
Как вылечилось:
1. запустил FixIt Tool (http://support.microsoft.com/kb/2739501)
2. выполнил дефрагментацию диска. Я реально не понимаю как она влияет, но такую рекомендацию на шёл на сайте MS. 


пятница, марта 07, 2014

Волшебство родной клавиатуры HTC One: textVisiblePassword и переключение языка

Хм. Это удивительно.

Если для EditText указать android:inputType="textVisiblePassword|textNoSuggestions", то при вводе в это поле при использовании родной клавиатуры HTC One кнопка смены языка остаётся незименной с англ. по дефолту.

Если textVisiblePassword убрать - переключается, но игнорирутеся textNoSuggestions: появляются предлагаемые варианты написания. Особенно удобно при вводе логинов и прочей закрытой информации.
Переключение языка есть,
предлагаются варианты



Переключение языка есть,
предлагаются варианты
(хотя и не должны)
 
Переключение языка отсутствует,
варианты не предлагаются


Переключение языка отсутствует,
варианты не предлагаются






пятница, февраля 21, 2014

How to enable Swype, SwiftKey keyboard on HTC One

To disable the HTC keyboard and to set custom one as default:
1. Go to Settings
2. Select Language & Keyboard
3. Turn Swype ON

Next:
4. From Applications menu disable HTC Sense Input

Now you can use Swype.  

пятница, декабря 06, 2013

ListView, не вызывается OnItemClickListener

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

Проблема заключается в том, что Button по умолчанию является focusable.
Решилось просто: берём Button и говорим ему:

        android:focusable="false"
        android:focusableInTouchMode="false"


вторник, ноября 26, 2013

Eclipse Kepler ADT, не запускается после падения, лечение / Eclipse Kepler does not start after crash

После (очередного) падения Eclipse по причине нехватки памяти он отказался запускаться

Выглядит это так:
1. кликаем на ярлык
2. наблюдаем секунду-две начальный экран
3. всё завершается.

В логе наблюдается следующая симптоматика:
!ENTRY org.eclipse.osgi 4 0 2013-11-26 11:48:30.590
!MESSAGE An error occurred while automatically activating bundle com.android.ide.eclipse.adt (414).
!STACK 0
org.osgi.framework.BundleException: Exception in com.android.ide.eclipse.adt.AdtPlugin.start() of bundle com.android.ide.eclipse.adt.

Запуск с параметром -clean не помог.

Вылечилось гуглом и следующим действием (отсюда):
delete .metadata\.plugins\org.eclipse.e4.workbench file




вторник, сентября 03, 2013

EditText - запрет подсказок при вводе

По идее, для того, чтобы запретить предлагать варианты в процессе ввода символов в EditText, достаточно указать флаг
android:inputType="textNoSuggestions"

Не знаю отчего, но на моём HTC One с клавиатурой от Swype наличие этого параметра не предотвращает от появления подсказок.

Помогло добавление ещё одного флага:
android:inputType="textVisiblePassword|textNoSuggestions"

среда, августа 21, 2013

Простой способ узнать размеры элементов после окончания раскладки

В Android – Using TouchDelegates увидел исключительно простой способ что-то сделать после того, как процесс раскладки компонентов завершён и известны их реальные размеры.


@Override
protected void onCreate(Bundle savedInstanceState) {
...

View content = findViewById(android.R.id.content);
content.post(r);
}

Runnable r = new Runnable() {

@Override
public void run() {
Log.d("TestApp", "width is:" + myView.getWidth());
}
};

понедельник, июня 24, 2013

Использование HierarchyViewer на нерутованных устройствах

В помощь: https://github.com/romainguy/ViewServer

Подключается так:

public class MyActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Set content view, etc.
        ViewServer.get(this).addWindow(this);
    }
      
    public void onDestroy() {
        super.onDestroy();
        ViewServer.get(this).removeWindow(this);
    }
  
    public void onResume() {
        super.onResume();
        ViewServer.get(this).setFocusedWindow(this);
    }
}

Я просто скопировал ViewServer.java в свой проект - чтобы не создавать зависимости между проектами, работает.