Table of contents

  1. Android: dim/darkness of a Dialog

    April 2021

  2. Testing MutableStateFlow without flakiness

    April 2021

  3. Mockito + kotlin Object + BitRise = fail

    April 2021

  4. Python: regex-based tokenizer in 4 lines of code

    April 2021

  5. Full-height BottomSheetDialogFragment

    March 2021

  6. Picasso, Glide, or Fresco

    March 2021

  7. Painless transparent status and navigation bar

    March 2021

  8. Android view crop: clipToPadding and clipChildren

    March 2021

  9. NVidia GT1030: how to run TensorFlow on Ubuntu 16.04

    March 2018

  10. ffmpeg: gapless video splitting and concatenation

    December 2016

  11. BUG: Android Kotlin dex transformation error

    February 2016

  12. Android ART can’t set final fields correctly

    January 2016

  13. The generated R class cannot be found (Android Annotations, AA and product flavors)

    May 2015

  14. Android transparent selector drawable doesn’t work

    May 2015

  15. How to Make Building with Gradle Faster

    October 2014

  16. Android .gitignore (Gradle + Android Studio)

    October 2014

  17. Compass + Yeoman

    October 2014

Content

Android: dim/darkness of a Dialog

class SomeFragment : BottomSheetDialogFragment() {
    override fun getTheme() = R.style.SomeThemeForBottomSheetDialog
<item name="android:backgroundDimAmount">0.35</item>
<style name="R.style.SomeThemeForBottomSheetDialog" parent="@style/ThemeOverlay.MaterialComponents.BottomSheetDialog">
    <item name="bottomSheetStyle">@style/SomeBottomSheet</item>
</style>
<style name="SomeBottomSheet" parent="@style/Widget.Design.BottomSheet.Modal">
    <item name="android:background">@android:color/transparent</item>
</style>

Testing MutableStateFlow without flakiness

  1. We need the observable itself:
private val _countdown = MutableStateFlow(0)
val countdown = _countdown.asStateFlow()

2. Now we need some collector, that we’ll need to use for testing:

val scope = CoroutineScope(Job() + Dispatchers.Main)
val countdownObserver = mock<FlowCollector<Int>>()

scope?.launch { countdown.collect(countdownObserver) }

3. Let’s test:

_countdown.value = 0
verify(countdownObserver).emit(0)

_countdown.value = 0
verify(countdownObserver).emit(0)

The complete snippet.

  1. It fails and it’s the correct behaviour.
  2. Dispatchers.Main is the must! Dispatchers.IO will give you a lot of flakiness!
package com.visa.mobile.feature.payments.usecase

import com.nhaarman.mockitokotlin2.clearInvocations
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.visa.mobile.common.TestCoroutineRule
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Rule
import org.junit.Test

@InternalCoroutinesApi
@ExperimentalCoroutinesApi
class MutableStateFlowTest {
    @get:Rule
    var coroutineRule = TestCoroutineRule()

    @Test
    fun `test MutableStateFlow`() = coroutineRule.runBlockingTest {
        val countdown = MutableStateFlow(0)
        val countdownObserver = mock<FlowCollector<Int>>()

        val scope = CoroutineScope(Job() + Dispatchers.Main)

        scope.launch { countdown.collect(countdownObserver) }

        countdown.value = 0; verify(countdownObserver).emit(0)
        clearInvocations(countdownObserver)
        countdown.value = 0; verify(countdownObserver).emit(0)

        scope.cancel()
    }
}

Mockito + kotlin Object + BitRise = fail

In one of my recent PRs, there was this small change, apart ~1000 other lines of code:

object MockData {
  ...
  val NOW = mock<Now> {
      on { ms } doReturn msTIMEOUT
      on { s } doReturn secTIMEOUT.toLong()
      on { date } doReturn Date(msTIMEOUT)
  }
  ...
}

It gave me lots of such errors on BitRise, though locally everything worked perfectly:

...
com.mycompany.packagename.ClassName100Test > Test case 100 FAILED
java.lang.NoClassDefFoundError at com.mycompany.packagename.ClassName100Test.kt:57

com.mycompany.packagename.ClassName101Test > Test case 101 FAILED
org.mockito.exceptions.misusing.UnfinishedStubbingException at ClassName101Test.kt:35

com.mycompany.packagename.ClassName102Test > Test case 102 FAILED
java.lang.NoClassDefFoundError at ClassName102Test.kt:43

com.mycompany.packagename.ClassName103Test > Test case 103 FAILED
java.lang.NoClassDefFoundError at ClassName103Test.kt:47

com.mycompany.packagename.ClassName104Test > Test case 104 FAILED
java.lang.NoClassDefFoundError at ClassName104Test.java:-2

com.mycompany.packagename.ClassName105Test > Test case 105 FAILED
java.lang.NoClassDefFoundError at ClassName105Test.java:-2

com.mycompany.packagename.ClassName106Test > Test case 106 FAILED
java.lang.NoClassDefFoundError at ClassName106Test.java:-2

com.mycompany.packagename.ClassName107Test > Test case 107 FAILED
java.lang.NoClassDefFoundError at ClassName107Test.java:-2
...

I can’t make any other conclusion but: avoid using Mockito mocking inside of Kotlin Objects.

Python: regex-based tokenizer in 4 lines of code

import re

SCANNER = re.compile(r'''
  (\s+) |                      # whitespace
  (//)[^\n]* |                 # comments
  0[xX]([0-9A-Fa-f]+) |        # hexadecimal integer literals
  (\d+) |                      # integer literals
  (<<|>>) |                    # multi-char punctuation
  ([][(){}<>=,;:*+-/]) |       # punctuation
  ([A-Za-z_][A-Za-z0-9_]*) |   # identifiers
  """(.*?)""" |                # multi-line string literal
  "((?:[^"\n\\]|\\.)*)" |      # regular string literal
  (.)                          # an error!
''', re.DOTALL | re.VERBOSE)

for match in re.finditer(SCANNER, data):
   space, comment, hexint, integer, mpunct, \
   punct, word, mstringlit, stringlit, badchar = match.groups()
   if space: ...
   if comment: ...
   # ... 
   if badchar: raise FooException...

Source

Full-height BottomSheetDialogFragment

val behavior = (requireDialog() as BottomSheetDialog).behavior
behavior.state = BottomSheetBehavior.STATE_EXPANDED
// behavior.peekHeight = 0

behavior.peekHeight = 0 causes a glitch: when I try to swipe the dialog to the bottom, it hides away, yet, still, the background is dimmed.

Picasso, Glide, or Fresco

I made a test with loading ~100mb of images (32 individual bitmaps) from /res/drawable-xxhdpi folder. I didn’t use imageView.setImageResource cause it loads images synchronously and blocks the UI thread. Overall, it takes ~50 minutes on Galaxy S3 to load everything. So the results:

  1. Fresco loads images also synchronously. Probably, it uses imageView.setImageResource under the hood.
  2. Glide is better. But it’s prefetching algorithm is somewhat different from what I expect. It seems like I’m always scrolling an empty RecyclerView, instead of seeing images.
  3. Picasso worked the best:
    1. Async image loading
    2. Less amount of code.
    3. Similarly to Glide, there’re no non-standard UI components (in comparison with Fresco).
    4. Nice loading prioritisation. In Splash activity I start prefetching, then, after ~5 seconds, on my RecyclerView activity I’m loading images with a higher priority. Thus, a user can scroll the list smoothly, while the prefetching is being loaded in the background.

So, finally, I adjusted the caching size to ~80% of free mem. It allowed to cache everything. Everything! So it loads super smoothly, it works super smoothly and it takes almost no code.

NVidia GT1030: how to run TensorFlow on Ubuntu 16.04

I used these guides, though it was a quite long dance, so I didn’t write everything down 🙁
1. https://www.nvidia.com/en-us/data-center/gpu-accelerated-applications/tensorflow/
2. http://www.python36.com/install-tensorflow141-gpu/
3. https://medium.com/@samnco/using-the-nvidia-gt-1030-for-cuda-workloads-on-ubuntu-16-04-4eee72d56791

Key points:
1. Need to install a graphic driver nvidia-375.
2. Don’t reboot.
3. Then need to install CUDA (9.1). It’ll also install nvidia-384, though it doesn’t make a login loop after that.
4. Reboot.
5. Compile TensorFlow from the source code.

The driver and CUDA are deb-based.

Good sign: your video adapter should work. Just installation of drivers (correct) – 90% of success. DO NOT download drivers or cuda from the official site. Only from official repos. See guide aboves for the details.
If have any questions, please, mail me: egslava@gmail.com

ffmpeg: gapless video splitting and concatenation

For instance you have a big video file and want to split it onto several parts all.mpg. Splitting it on 1 sec parts:

ffmpeg -i all.mp4 -ss 0 -t 1 first-1-sec.mp4
ffmpeg -i all.mp4 -ss 1 -t 1 first-2-sec.mp4
ffmpeg -i all.mp4 -ss 2 -t 1 first-3-sec.mp4

After this you can want to check the result by combining these files together in one:

One alternative is:

ffmpeg -i "concat:first-1-sec.mp4|first-2-sec.mp4|first-3-sec.mp4" -c copy blah.mp4

But it works for me not always. So there’s also another one:

  1. Creating files.txt:

    file first-1-sec.mp4
    file first-2-sec.mp4
    file first-3-sec.mp4
  2. Running
    ffmpeg -f concat -i files.txt  blah.mp4

And here we’re!

BUG: Android Kotlin dex transformation error

Today I got strange error on HelloWorld-like project:

Error:Execution failed for task ‘:app:transformClassesWithDexForDebug’.
> com.android.build.api.transform.TransformException: java.lang.RuntimeException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process ‘command ‘/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/bin/java” finished with non-zero exit value 1

Continue…

Android ART can’t set final fields correctly

For example you have class Person. Person is a POJO for REST-service (for instance, for using with retrofit).

class User {
  public static final String firstName = "", lastName = "";
}

This line of code creates two read-only fields. It’s necessary to assign initial values because every field is final. But there’s another way to do the same thing:

class User {
  public static final String firstName, lastName;

  public User() {
    firstName = "";
    lastName = "";
  }
}

It’s hard to believe, but second – is the only right way to create class.

For example, you’re creating class instance:

User user = SomeReflection.getUser();
// user.lastName.equals("Username")
callFunction(user); // in this function user.lastName.equals("") for 1st method

I think it depends on JVM but it’s truth for Android ART.

How to Make Building with Gradle Faster

In the root of your project (in the directory with settings.gradle) create or just open file local.properties and add these two lines:

org.gradle.parallel=true
org.gradle.daemon=true

Compass + Yeoman

There’s an elegant method to connect Compass to Yeoman I couldn’t find in the web.
One line of code
So I just leave this here.

Continue…