Category: Android Development

  • Make a RecyclerView expand its height when new item is added //Android Dev Diary

    It seems that simply using android:layout_height="wrap_content" on your RecyclerView won’t make its height automatically expand when new items are added in it.

    (more…)
  • How to start using view binding //Android Kotlin Dev Diary

    First, open your app/build.gradle file

    Inside the android{} block of app/build.gradle, add the following:

    buildFeatures { viewBinding = true }

    Sync project with gradle files.

    In your activity or fragment, define the following class-wide property:

    private lateinit var binding: FragmentorActivityNameBinding

    Replace the FragmentorActivityNameBinding above with the appropriate binding class name. Tip: The binding class name is based on the XML file’s name. For example, if your XML file name is activity_main.xml, the binding class name should be ActivityMainBinding. Or, if your file name is fragment_main.xml, the binding class name should be FragmentMainBinding.

    In your activity, the onCreate function should look like this:

    override fun onCreate(savedInstanceState: Bundle?) {
    
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)
    }

    Otherwise, if using view binding in a fragment, your onCreateView should look like this:

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
    
        binding = FragmentMainBinding.inflate(inflater, container, false)
        binding.lifecycleOwner = viewLifecycleOwner
        binding.viewModel = mainViewModel
        return binding.root
    }
  • Disable animations programmatically during instrumented tests // Android Dev Diary

    When running an instrumented Android test, or “androidTest”, and you encounter the following error:

    androidx.test.espresso.PerformException: Error performing 'scroll to' on view 'Animations or transitions are enabled on the target device.

    It means that you need to disable animations for it to successfully run the test.

    But, it can take a lot of clicks on the emulator/device screen to get to the developer settings to turn off animations!

    So, for a quicker way to disable animations in the Android device during development, just enter the following commands in your Android Studio terminal:

    adb shell settings put global window_animation_scale 0.0
    adb shell settings put global transition_animation_scale 0.0
    adb shell settings put global animator_duration_scale 0.0
  • Exception: No signature of method android() is applicable for argument types, Build Gradle // Dev Diary

    Today I wanted to make a data class a Parcelable so that I could pass it as an argument in Navigation component.

    Parcelables, though better in performance than Serializables, take more time to implement! To help us out, there’s a “kotlin-parcelize” plugin that can generate the boilerplate code for us.

    There were issues though.

    After applying the kotlin-parcelize plugin, I was getting the error:

    'kotlin-parcelize' can't be applied together with 'kotlin-android-extensions'

    So kotlin-parcelize is like a “successor” to the now deprecated 'kotlin-android-extensions' that I’m still using. So I replaced 'kotlin-android-extensions' with ‘kotlin-parcelize’.

    I got another error after attempting to sync with my Gradle files:

    org.gradle.api.GradleScriptException: A problem occurred evaluating project ':app'
    [...]
    Caused by: groovy.lang.MissingMethodException: No signature of method: build_54gmhuqjm83le8li4fspjsxlu.android() is applicable for argument types: (build_54gmhuqjm83le8li4fspjsxlu$_run_closure1) values: [build_54gmhuqjm83le8li4fspjsxlu$_run_closure1@3c374eea]
    	at build_54gmhuqjm83le8li4fspjsxlu.run([...]\app\build.gradle:11)
    

    The solution was to remove the following code from my android{} block:

    androidExtensions {
        experimental = true
    }

    After removing that, Gradle synced successfull!

    References:
    Parcelable implementation generator
    How to use @Parcelize now that kotlin-android-extensions is being deprecated?

  • Two-way data binding with StateFlow & BindingAdapters //Dev Diary

    I created BindingAdapters for an EditText like so:

    @BindingAdapter("android:text")
    fun setText(editText: EditText, inputAmount: MutableStateFlow<Double>) {
        if (editText.text.toString() != inputAmount.value.toString()) {
            editText.setText(inputAmount.value.toString())
            editText.setSelection(editText.text.length)
        }
    }
    
    @InverseBindingAdapter(attribute = "android:text")
    fun getText(editText: EditText): Double {
        if (editText.text.isEmpty()) return 0.0
        return editText.text.toString().toDouble()
    }
    
    

    My XML layout element looks like this:

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/tiet_input_amount"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/input_hint_amount"
                    android:inputType="numberDecimal"
                    android:text="@={viewModel._inputAmount}" />

    And in my ViewModel, the inputAmount is set up this way:

        val _inputAmount: MutableStateFlow<Double> = MutableStateFlow(0.0)
        val inputAmount: StateFlow<Double> = _inputAmount
  • Grant, revoke permissions via Android Studio Terminal (ADB Shell) // Android Dev Diary

    To grant and revoke permissions via Terminal (ADB Shell), run your app on an emulator (I haven’t personally tried on a device but it should work there too).

    Open your Terminal, and then enter the following command:

    adb shell

    Once you’re in, you may use the following syntax for granting or revoking your Android device’s permissions:

    pm grant <package-name> <permission>

    For example, to grant a permission:

    pm grant net.catzie.myapp android.permission.READ_EXTERNAL_STORAGE

    pm grant net.catzie.myapp android.permission.WRITE_EXTERNAL_STORAGE

    And to revoke a permission:

    pm revoke net.catzie.myapp android.permission.READ_EXTERNAL_STORAGE

    pm revoke net.catzie.myapp android.permission.WRITE_EXTERNAL_STORAGE

    Now there’s no need to open the settings screen on your device to play with your Android permissions!

  • Remove room database on app uninstall // Android Dev Diary

    The problem:

    Installing on an Android device that has an old version of an app I’m developing triggers an error:

    Room Database Migration: java.lang.IllegalStateException: Migration didn't properly handle

    Problem persists despite uninstalling the Android app and deleting all app data.

    The solution:

    Very simple. I set allowBackup to false in my AndroidManifest.xml file like so:

    android:allowBackup="false"

    Then I reinstalled the app on the Android device. Error was still triggered on first install, but after one more round of uninstall and reinstall, the error finally went away!

    Side Note / Quick Life Update:

    I have been working on an app, with me as its first user. I tentatively call it “bodymonitor”. It should let me log foods I ate, meds/supplements I took, and body sensations. My brain does not do a good job of monitoring these things so I shall get an app to help me do it. There are existing apps out there that do more or less the same thing, but I want one that caters to my specific needs. Doesn’t hurt to practice Android app development as I make the app, too!

    This weekend I’ll undergo endoscopy again, but this time it’s called a “flexible nasopharyngolaryngoscopy” which will pass through the nose instead of the mouth, to be done under local anesthesia. From the sound of it I don’t expect to be put to sleep, which makes me less anxious, but still anxious. XD It’s gonna be done because of my throat issues.