dimitrilc 30 Junior Poster
Introduction

In this tutorial, we will learn how to apply Material 3 colors and Dynamic Colors to our App.

Goals

At the end of the tutorial, you would have learned:

  1. How to apply Material 3 colors.
  2. How to enable Dynamic Colors.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 3.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Remove the default TextView.

  3. Copy and paste the code below into activity_main.xml. This will add a couple of random widgets mainly for us to see the visual effects. The code inside is not really important.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"
            app:layout_constraintBottom_toTopOf="@+id/switch1"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <androidx.appcompat.widget.SwitchCompat
            android:id="@+id/switch1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Switch"
            app:layout_constraintBottom_toTopOf="@+id/radioGroup"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button" />
    
        <RadioGroup
            android:id="@+id/radioGroup"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/switch1">
    
            <RadioButton
                android:id="@+id/radioButton"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="RadioButton" />
    
            <RadioButton
                android:id="@+id/radioButton2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="RadioButton" />
    
        </RadioGroup>
     </androidx.constraintlayout.widget.ConstraintLayout>
  4. Inside of the Module build.gradle, change the version of com.google.android.material:material to 1.6.0-rc01.

Change theme to Material 3

When the project was first created, Android Studio automatically chose the Theme.MaterialComponents.DayNight.DarkActionBar theme for us. Themes starting with Theme.MaterialComponents.* actually belongs to Material 2, so in order to use a Material 3 theme, we must inherit from a Theme.Material3.* theme instead.

Fortunately, there is …

dimitrilc 30 Junior Poster
Introduction

In Android development, UseCases are classes that encapsulate business logic that are often used in ViewModel classes. UseCases belong to the optional Domain layer in Android apps, so they are not required, but can reduce your ViewModel’s complexity and make your application easier to test.

In this tutorial, we will learn how to add UseCases into an Android app.

Goals

At the end of the tutorial, you would have learned:

  1. How to add UseCases.
  2. How to use the special invoke() operator with a UseCase.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 3.
Prerequisite Knowledge
  1. Intermediate Android.
  2. Basic MVVM architecture.
  3. StateFlow.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Replace the content of activitiy_main.xml with the code below. This simply adds three new TextViews and a Button in a vertical chain.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/button_loadNext"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/load_next_item"
            app:layout_constraintBottom_toTopOf="@+id/textView_item1"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <TextView
            android:id="@+id/textView_item1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toTopOf="@+id/textView_item2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button_loadNext"
            tools:text="Item 1" />
    
        <TextView
            android:id="@+id/textView_item2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toTopOf="@+id/textView_item3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView_item1"
            tools:text="Item 2" />
    
        <TextView
            android:id="@+id/textView_item3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView_item2"
            tools:text="Item 3" />
    
     </androidx.constraintlayout.widget.ConstraintLayout>
  3. Add the <string> resource below into your strings.xml file.

     <string name="load_next_item">Load Next Item</string>
  4. Add the dependency to Lifecycle and Activity KTX below into your …

dimitrilc 30 Junior Poster
Introduction

When working with WorkManager, it is important to know how to provide input data to your Workers. In this tutorial, we will learn how to provide basic input data to a Worker as well as when Workers are chained together.

Goals

At the end of the tutorial, you would have learned:

  1. How to provide input data to Workers.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 3.
Prerequisite Knowledge
  1. Intermedia Android.
  2. Basic WorkManager.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the dependency to WorkManager library below into your Module build.gradle file.

     //Kotlin Worker
     implementation "androidx.work:work-runtime-ktx:2.7.1"
  3. Replace activity_main.xml with the code below. This adds a Button to start the Workers later in the tutorial.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/button_startWorker"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button_start_worker"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  4. Add the <string> resource below into strings.xml.

     <string name="button_start_worker">Start Worker</string>
  5. Create a new class called DownloadWorker with the code below. This Worker does not do anything yet and simply returns Result.sucess() at this point.

     class DownloadWorker(appContext: Context, workerParams: WorkerParameters) :
        Worker(appContext, workerParams) {
    
        override fun doWork(): Result {
            return Result.success()
        }
    
     }
  6. In MainActivity, append the code below into the function onCreate(). This binds the Button’s OnClickListener to enqueue a Work …

dimitrilc 30 Junior Poster
Introduction

When using a Worker (from the WorkManager library), you might have wondered how to inject dependencies with Hilt. In this tutorial, we will learn how to inject dependencies into our Worker.

Goals

At the end of the tutorial, you would have learned:

  1. How to inject dependencies into a Worker.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 2.
Prerequisite Knowledge
  1. Intermediate Android.
  2. WorkManager library.
  3. Hilt.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the plugins below into the plugins{} block of the module build.gradle file.

     id 'kotlin-kapt'
     id 'dagger.hilt.android.plugin'
  3. Add the dependencies below into the module build.gradle file. These are Hilt and WorkManager dependencies.

     //Work Manager
     implementation 'androidx.work:work-runtime-ktx:2.7.1'
     //Hilt
     implementation 'com.google.dagger:hilt-android:2.41'
     implementation 'androidx.hilt:hilt-work:1.0.0'
     kapt 'com.google.dagger:hilt-compiler:2.41'
     kapt 'androidx.hilt:hilt-compiler:1.0.0'
  4. Add the Hilt Gradle plugin dependency to the Project build.gradle file.

     buildscript {
        repositories {
            google()
            mavenCentral()
        }
        dependencies {
            classpath 'com.google.dagger:hilt-android-gradle-plugin:2.41'
        }
     }
  5. Create an empty class called ExampleDependency in a file called ExampleDependency.kt. This will act as the dependency that we would later inject into our Worker.

     class ExampleDependency @Inject constructor()
  6. Create a class called MyApplication that extends Application, and then annotate it with @HiltAndroidApp.

     @HiltAndroidApp
     class MyApplication: Application() {
     }
  7. Add the Application class name into your manifest’s <application>.

     android:name=".MyApplication"
dimitrilc 30 Junior Poster
Introduction

Finding a View in Espresso tests can be quite confusing because there are so many matchers available. In this tutorial, we will learn how to find a View based on its sibling contents.

Goals

At the end of the tutorial, you would have learned:

  1. How to match against siblings in Espresso tests.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 2.
Prerequisite Knowledge
  1. Basic Android.
  2. Basic Espresso.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Replace the default activity_main.xml with the code below. This replaces the default TextView with a RecyclerView, constrains it, and assign it an id.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView_myRecycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  3. Create a new sample layout called sample_viewholder.xml to act as the View for each RecyclerView item. This layout includes 3 TextView objects placed in a horizontal chain.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/layout_viewHolder"
        android:layout_marginVertical="16dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <TextView
            android:id="@+id/textView_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/textView_title"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Name" />
    
        <TextView
            android:id="@+id/textView_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/textView_age"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toEndOf="@+id/textView_name"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Title" />
    
        <TextView
            android:id="@+id/textView_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/textView_title"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Age" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  4. Create a new data class called SampleViewHolderUiState to hold the UI state for the sample_viewholder.xml.

     data class SampleViewHolderUiState(
        val name: String,
        val title: …
dimitrilc 30 Junior Poster
Introduction

When working with Room, you might have wondered how to run multiple statements in a single transaction. Running multiple statements in one transaction has two main benefits:

  1. Your statements can reuse the same connection.
  2. They can all fail together if something goes wrong.

Regarding the first benefit, it is not well documented whether Room keeps the connection alive or closes them after every statement. Developers normally do not have to worry about manually closing database connections when using Room. Upon close inspection of the RoomDatabase source code, it appears that RoomDatabase does have a property called mAutoCloser that is used to run transactions with, at least when it is not null.

public void beginTransaction() {
    assertNotMainThread();
    if (mAutoCloser == null) {
        internalBeginTransaction();
    } else {
        mAutoCloser.executeRefCountingFunction(db -> {
            internalBeginTransaction();
            return null;
        });
    }
}

From the AutoCloser source code, this class is described as:

AutoCloser is responsible for automatically opening (using delegateOpenHelper) and closing (on a timer started when there are no remaining references) a SupportSqliteDatabase.

Because we now know that AutoCloser exists, we are going to assume that Room opens and closes the database connection for every transaction (maybe not immediately, but with Handler#postDelayed()), and this opening and closing can be expensive. If the situation applies, we should wrap multiple statements into a single transaction.

There are two different ways, that I am aware of, to run multiple statements in a transaction, using the Dao or the convenient methods from the RoomDatabase class. …

dimitrilc 30 Junior Poster
Introduction

When working with Room, you might have wondered how to describe one-to-many relationships between entities. In this tutorial, we will learn how to do just that.

Goals

At the end of the tutorial, you would have learned:

  1. How to define one-to-many relationship for entities in Room.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 2.
Prerequisite Knowledge
  1. Intermediate Android.
  2. SQL.
  3. Basic Room database.
  4. Kotlin coroutines.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the dependencies below for Room into the Module build.gradle.

     def room_version = "2.4.2"
      implementation "androidx.room:room-runtime:$room_version"
      kapt "androidx.room:room-compiler:$room_version"
      implementation "androidx.room:room-ktx:$room_version"
      implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
  3. In the same file, add the kapt plugin under plugins

     id 'kotlin-kapt'
  4. Create a ClassRoom entity using the code below.

     @Entity(tableName = "class_room")
     data class ClassRoom(
        @PrimaryKey(autoGenerate = true)
        @ColumnInfo(name = "class_room_id")
        val classRoomId: Long = 0
     )
  5. Create a new Student entity using the code below.

     @Entity(tableName = "student")
     data class Student(
        @PrimaryKey(autoGenerate = true)
        @ColumnInfo(name = "student_id")
        val studentId: Long = 0,
        val name: String,
        val age: Int
     )
  6. Create a new StudentDao using the code below.

     @Dao
     interface StudentDao {
        @Insert
        suspend fun insertStudents(vararg students: Student)
     }
  7. Create a new ClassRoomDao using the code below.

     @Dao
     interface ClassRoomDao {
        @Insert
        suspend fun insertClassRoom(classRoom: ClassRoom)
     }
dimitrilc 30 Junior Poster
Introduction

There are many ways to describe relationships between Entities in Room, one of which is to embed an entity inside of another. When embedding an Entity, the columns of the embedded Entity are extracted as member columns of the enclosing entity.

In this tutorial, we will learn how to embed an Entity in a Room database.

Goals

At the end of the tutorial, you would have learned:

  1. How to embed an Entity inside of another Entity.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 2.
Prerequisite Knowledge
  1. Intermediate Android.
  2. SQL.
  3. Basic Room database.
  4. Kotlin coroutines.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the dependencies below for Room into the Module build.gradle.

     def room_version = "2.4.2"
     implementation "androidx.room:room-runtime:$room_version"
     kapt "androidx.room:room-compiler:$room_version"
     implementation "androidx.room:room-ktx:$room_version"
     implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
  3. In the same file, add the kapt plugin under plugins

     id 'kotlin-kapt'
  4. Create a new Kotlin data class for an Entity called SoccerTeam using the code below.

     @Entity(tableName = "soccer_team")
     data class SoccerTeam(
        @PrimaryKey(autoGenerate = true) val id: Long
     )
  5. Create a new Kotlin data class for an Entity called HeadCoach using the code below.

     @Entity(tableName = "head_coach")
     data class HeadCoach(
        @PrimaryKey(autoGenerate = true)  val id: Long,
        val name: String,
        val age: Int,
     )
  6. Create a new Dao for SoccerTeam using the …

dimitrilc 30 Junior Poster
Introduction

When working with Services on Android, you might have ran into an issue where you would like to:

  1. Use a concrete implementation of a Service (from a built-in or third party library).
  2. Make the Service lifecycle-aware so that you can use coroutines with it. Many built-in Service classes are in Java and were introduced pre-Kotlin era.

As of right now, the only built-in Service class from Android that implements LifecycleOwner is LifecycleService. But LifecycleService is a concrete implementation, so you cannot extend both from it and another concrete Service.

We can opt in to use composition by having a LifecycleService as a member of our own Service, but there are couple of downsides to this approach:

  1. We will have to be very careful to pass the correct lifecycle calls to the member LifecycleService.
  2. Other components are not aware that our Service is now lifecycle-aware.
  3. I have not seen this pattern mentioned anywhere, so this approach is not well documented and is most likely anti-pattern.
  4. There is a better approach, which is to implement LifecycleOwner directly. This option is officially supported by Android.

In this tutorial, we will learn how to implement LifecycleOwner in our own Service.

Goals

At the end of the tutorial, you would have learned:

  1. How to implement LifecycleOwner in a Service.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 2.
Prerequisite Knowledge
  1. Intermediate Android.
  2. Kotlin.
  3. Android Services.
Project …
dimitrilc 30 Junior Poster
Introduction

When working with Room, there might come a situation during bulk CRUD operations that it would be useful to add a Foreign Key to one of our tables. This provides two main benefits: cascading delete (must be configured manually) and enforcing data integrity on linked entities.

In this tutorial, we will learn how to add Foreign Keys into Room entities.

Goals

At the end of the tutorial, you would have learned:

  1. How to add Foreign Keys to Room entities.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 2.
Prerequisite Knowledge
  1. Intermediate Android.
  2. SQL.
  3. Basic Room database.
  4. Kotlin coroutines.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the dependencies below for Room into the Module build.gradle.

     def room_version = "2.4.2"
     implementation "androidx.room:room-runtime:$room_version"
     kapt "androidx.room:room-compiler:$room_version"
     implementation "androidx.room:room-ktx:$room_version"
     implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
  3. In the same file, add the kapt plugin under plugins

     id 'kotlin-kapt'
  4. Create a new @Entity called Student with the code below.

     @Entity
     data class Student(
        @PrimaryKey(autoGenerate = true) val id: Long = 0,
        @ColumnInfo(name = "first_name") val firstName: String,
        @ColumnInfo(name = "last_name") val lastName: String
     )
  5. Create another @Entity called ReportCard with the code below.

     @Entity(tableName = "report_card")
     data class ReportCard(
        @PrimaryKey(autoGenerate = true) val id: Long = 0,
        @ColumnInfo(name = "student_id") val studentId: Long
     )
  6. Create …

dimitrilc 30 Junior Poster
Introduction

If your Android Application uses Room, then you might have wondered how to prepopulate a Room database. In this tutorial, we will learn how to prepopulate a Room database.

Goals

At the end of the tutorial, you would have learned:

  1. How to prepopulate a Room database.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 2.
Prerequisite Knowledge
  1. Basic Android.
  2. Basic Room.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the dependencies below for Room into the Module build.gradle.

     def room_version = "2.4.2"
     implementation "androidx.room:room-runtime:$room_version"
     kapt "androidx.room:room-compiler:$room_version"
     implementation "androidx.room:room-ktx:$room_version"
     implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
  3. In the same file, add the kapt plugin under plugins.

     id 'kotlin-kapt'
  4. Add a new Person Entity with the code below.

     @Entity(tableName = "person")
     data class Person(
        @PrimaryKey(autoGenerate = true) val id: Long,
        @ColumnInfo(name = "first_name") val firstName: String,
        @ColumnInfo(name = "last_name") val lastName: String
     )
  5. Add a new Person DAO with the code below.

     @Dao
     interface PersonDao {
        @Query("SELECT * FROM person WHERE id=:id")
        suspend fun getPersonById(id: Long): Person?
     }
  6. Create a new Room database with the code below.

     @Database(entities = [Person::class], version = 1)
     abstract class MyDatabase : RoomDatabase(){
        abstract fun personDao(): PersonDao
     }
  7. In MainActivity#onCreate(), append the code below. This creates a dummy connection to the database at launch so …

dimitrilc 30 Junior Poster
Introduction

When working with music files, you might have wondered how to load display art from Audio files. In this tutorial, we will learn how to load thumbnails for audio files.

Goals

At the end of the tutorial, you would have learned:

  1. How to load display art for music files.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 2.
Prerequisite Knowledge
  1. Basic Android.
  2. MediaStore. For simplicity, all queries in this tutorial are performed on the UI thread. In real code, prefer coroutines.
  3. ActivityResults API.
  4. Permissions.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.
    Replace activity_main.xml with the content below. This removes the default “Hello World!” TextView, adds a Button and an ImageView.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/button_loadMusic"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/load_music"
            android:layout_marginVertical="16dp"
            app:layout_constraintBottom_toTopOf="@+id/imageView_displayArt"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <ImageView
            android:id="@+id/imageView_displayArt"
            android:layout_width="300dp"
            android:layout_height="300dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button_loadMusic"
            tools:srcCompat="@tools:sample/backgrounds/scenic" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  2. Add the <string> resource below into strings.xml.

     <string name="load_music">Load Music</string>
  3. Download the sample music song I Move On by Jan Morgenstern (© copyright Blender Foundation | durian.blender.org). Upload the file onto your test device.

  4. In AndroidManifest.xml, add the <uses-permission> element inside of <manifest>, but outside of <application>.

      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Project Overview

Currently, our project only has two elements: a Button and an ImageView.

dimitrilc 30 Junior Poster
Introduction

Android provides many ways to perform background tasks, such as Services, WorkManager, or even threads. In this tutorial, we will learn how to create a Service for our Android app.

Goals

At the end of the tutorial, you would have learned:

  1. How to create a Service.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 2.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Replace activity_main.xml with the code below. This removes the default “Hello World!” Textview and adds two Buttons for starting and stopping the Service.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/button_startService"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/start_service"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/button_stopService"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <Button
            android:id="@+id/button_stopService"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/stop_service"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toEndOf="@+id/button_startService"
            app:layout_constraintTop_toTopOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  3. Add the <string> resources below into strings.xml.

     <string name="start_service">Start Service</string>
     <string name="stop_service">Stop Service</string>
  4. In MainActivity#onCreate(), obtain a reference to button_startService.

     val startServiceButton = findViewById<Button>(R.id.button_startService)
  5. Obtain a reference to button_stopService as well.

     val stopServiceButton = findViewById<Button>(R.id.button_stopService)
Creating the Service class

A service is a class that extends Android.app.Service. From the Service class, the only required function that we must override is the onBind() function. There are other functions such as onStartCommand() or onDestroy() for us to override as well.

There are three different types of …

dimitrilc 30 Junior Poster

Hello EmiLynn, here are a couple of hints for you.

  1. Your teacher's instruction says to return a map of the name of each mammal and the sum of the ages, so you need to return Map<String, Int> instead of Map<String, Mammal>. This will allow the compiler to help you out.
  2. I am not sure why the sortedBy() is used here. You did not tell us that it is a requirement. Remove it if it's not needed because it can further confuse you.
  3. The equivalence of Java Stream peek() in Kotlin would be onEach(). You can add onEach() after each intermediate step to print out the current values; this would greatly help with debugging, so you can see how your data structure mutates.
  4. Write down in English what needs to happen at each step if you want to transform List<Mammal> into Map<String, Int>; draw pictures if you need to. Code each step with simple functions. Create temporary data structures, etc. You do not have to use fancy operators like associateBy() at first. After you have successfully produced the result with simple functions, read through the list of operators again to see how you can simplify your code with only one or two operators.
dimitrilc 30 Junior Poster
Introduction

When working with Hilt, you might have wondered how to inject ViewModels into your application. In this tutorial, we will learn how to inject ViewModels into your app Fragments.

Goals

At the end of the tutorial, you would have learned:

  1. How to inject ViewModels into Fragments.
  2. Understand injected ViewModel’s lifecycle.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 1.
Prerequisite Knowledge
  1. Basic Android.
  2. Basic Hilt.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Create 3 new Fragments called BlankFragment1, BlankFragment2, and BlankFragment3 by right-clicking the main package > New > Fragment > Fragment (Blank).

  3. Replace the code inside activity_main.xml with the code below. This removes the default “Hello World!” TextView, adds three Buttons aligned on a vertical chain, and a FragmentContainerView.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/button_toFragment1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/to_fragment_1"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <Button
            android:id="@+id/button_toFragment2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/to_fragment_2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button_toFragment1" />
    
        <Button
            android:id="@+id/button_toFragment3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/to_fragment_3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button_toFragment2" />
    
        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/fragmentContainerView"
            android:name="com.codelab.daniwebhiltviewmodels.BlankFragment1"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/button_toFragment3" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  4. Add the code below into MainActivity#onCreate(). It binds the three buttons to an action that will always create the respective Fragment, without any backstack.

     val toFragment1Button = findViewById<Button>(R.id.button_toFragment1)
     val toFragment2Button = findViewById<Button>(R.id.button_toFragment2)
     val toFragment3Button = findViewById<Button>(R.id.button_toFragment3)
    
     toFragment1Button.setOnClickListener{
        supportFragmentManager.commit {
            replace<BlankFragment1>(R.id.fragmentContainerView)
        } …
nd74850 commented: yes +0
dimitrilc 30 Junior Poster
Introduction

In Android development, reusable layouts can be used to reduce duplicate code across XML files. In this tutorial, we will learn how to reuse layout files, especially with View Binding enabled.

Goals

At the end of the tutorial, you would have learned:

  1. How to reuse layouts.
  2. How to use View Binding with reusable layouts.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 1.
Prerequisite Knowledge
  1. Basic Android.
  2. Basic ViewBinding.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Give the default “Hello World!” TextView an android:id of textView_default.

  3. Create another layout resource file called reusable_layout.xml. Copy and paste the code below to replace all of the content of this file. This code removes the default ConstraintLayout because it is not necessary for our use case. It also adds a TextView with an id.

     <?xml version="1.0" encoding="utf-8"?>
     <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/textView_reusable"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView" />
  4. Enable View Binding in the Module gradle.build file, inside of the android {} tag. This will instruct the compiler to generate Binding classes at development time.

     buildFeatures {
         viewBinding true
     }
  5. Add the binding to activity_main.xml in MainActivity with the code below.

     private lateinit var binding: ActivityMainBinding
  6. Replace the current onCreate() call with the code below. We now inflate the layout using View Binding instead of …

dimitrilc 30 Junior Poster
Introduction

MediaPlayer (android.media.MediaPlayer) is a popular way to play media files, and combining it with a SeekBar can greatly improve the user experience. In this tutorial, we will learn how to synchronize a MediaPlayer progress to a SeekBar position.

Goals

At the end of the tutorial, you would have learned:

  1. How to sync an active MediaPlayer to a SeekBar.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 1.
Prerequisite Knowledge
  1. Intermediate Android.
  2. ActivityResult APIs.
  3. Storage Access Framework (SAF).
  4. Coroutines.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Give the default “Hello World!” TextView android:id of textView_time.

  3. Completely remove the android:text attribute from textView_time.

  4. Add the tools:text attribute to textView_time with the value of 0:00.

  5. Add the android:textSize attribute with the value of 32sp.

  6. Constraint textView_time to the top, start, and end of ConstraintLayout, but leave the bottom side unconstrained.

  7. Your textView_time should look like the code below.

     <TextView
        android:id="@+id/textView_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="32sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="0.00" />
  8. Download the .mp3 file called Unexpected Gifts of Spring by Alextree from the FreeMusicArchive. This song is licensed under CC BY 4.0. You are recommended to download the file from the AVD’s built-in web browser. If you have downloaded the file to your development machine, you can also drag …

dimitrilc 30 Junior Poster
Introduction

When working with a Room database, we are mostly restricted to save data using primitives (and boxed primitives). Reference types are not supported right out of the box, but can be enabled by creating additional TypeConverter. If you are familiar with ORM-light frameworks such as Spring JDBC, then you can think of TypeConverters as being conceptually similar to RowMapper.

In this tutorial, we will learn how to use TypeConverters in a Room database. Existing basic knowledge of Room is required for this tutorial.

Goals

At the end of the tutorial, you would have learned:

  1. How to create TypeConverters to save reference types in a Room database.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 1.
Prerequisite Knowledge
  1. Basic Android.
  2. Basic Room.
  3. Basic Serialization.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the dependencies below into your Module build.gradle file inside the dependencies {} block.

     def roomVersion = "2.4.1"
    
     implementation "androidx.room:room-runtime:$roomVersion"
     annotationProcessor "androidx.room:room-compiler:$roomVersion"
    
     //To use Kotlin annotation processing tool (kapt)
     kapt "androidx.room:room-compiler:$roomVersion"
    
     //Kotlin Extensions and Coroutines support for Room
     implementation "androidx.room:room-ktx:$roomVersion"
    
     //lifecycle scope
     implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
  3. In the same file, add the kapt annotation processor inside the plugins {} block.

     id 'org.jetbrains.kotlin.kapt'
  4. Create a new file called Teacher.kt and add the code below.

     data class Teacher(
        val name: String,
        val age: …
dimitrilc 30 Junior Poster
Introduction

In this tutorial, we will learn how to add swipe-to-remove and drag-to-reorder functionalities into RecyclerView.

Goals

At the end of the tutorial, you would have learned:

  1. How to add swipe-to-remove and drag-to-reorder functionality to a RecyclerView.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 1.
Prerequisite Knowledge
  1. Basic Android.
  2. Basic RecyclerView.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Replace the content of activity_main.xml with the code below.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            app:layout_constraintTop_toTopOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  3. Create a new layout resource called item_view.xml. This is the ViewHolder’s layout. Copy and paste the code below.

     <?xml version="1.0" encoding="utf-8"?>
     <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/itemView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:gravity="center"
        android:textSize="32sp"
        tools:text="TextView" />
  4. Create a new Kotlin file called Item.kt. Copy and paste the code below.

     data class Item(
        val content: String
     )
  5. Create a new Kotlin file called ItemAdapter.kt. Copy and paste the code below.

     import android.view.LayoutInflater
     import android.view.View
     import android.view.ViewGroup
     import android.widget.TextView
     import androidx.recyclerview.widget.RecyclerView
    
     class ItemAdapter(private val dataset: List<Item>): RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {
    
        class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
            val textView: TextView = itemView.findViewById(R.id.itemView)
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
            val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.item_view, parent, false)
    
            return ItemViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
            holder.textView.text = …
dimitrilc 30 Junior Poster
Introduction

When using Navigation Components, you might have wondered how to pass arguments between destinations. We will learn how to do just that in this tutorial. This tutorial also builds upon the Basic Navigation tutorial, so if you are not familiar with Navigation Components, I would recommend you to check it out.

There are two parts to this tutorial. First, we will learn how to perform navigation with type safety. Secondly, we will learn how to pass arguments with Safe Args.

Goals

At the end of the tutorial, you would have learned:

  1. How to use Navigation with Type Safety.
  2. How to pass arguments between destinations with Safe Args.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 1.
Prerequisite Knowledge
  1. Basic Android.
  2. Basic Navigation.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Remove the default “Hello World!” TextView.

  3. In the Project-level build.gradle file, add the buildscript{} block below. Make sure that you place it above the existing plugins{} block.

     buildscript {
        repositories {
            google()
        }
        dependencies {
            def nav_version = "2.4.0"
            classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
        }
     }
  4. In the Module-level build.gradle file, add the dependencies below to the dependencies{} block.

     def nav_version = "2.4.0"
    
     implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
     implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
  5. Also in the Module-level build.gradle file, add the plugin below into the plugins{} block.

     id …
dimitrilc 30 Junior Poster
Introduction

If you are working on a native Android app using the View system, then you might have come across a situation where you would need to add a Composable (androidx.compose.runtime.Composable) into your View hierarchy. In this tutorial, we will learn how to add a Composable into an existing View system.

The Composables that we will use in this tutorial will come from the Material 3 library.

Goals

At the end of the tutorial, you would have learned:

  1. How to add a Composable into a View system.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1.
Prerequisite Knowledge
  1. Intermediate Android.
  2. Basic Jetpack Compose.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Remove the default “Hello World!” TextView from activity_main.xml.

  3. Inside of your module build.gradle, upgrade your module’s Material dependency to version 1.5.0.

     implementation 'com.google.android.material:material:1.5.0'
  4. Add the variable below to hold the compose version.

     def composeVersion = '1.0.5'
  5. Add these options to your android build options (the android{} block)

     buildFeatures {
         compose true
     }
    
     composeOptions {
         kotlinCompilerExtensionVersion = composeVersion
     }
  6. Add the Compose dependencies below into your dependencies{} block.

     //Compose
     implementation "androidx.compose.runtime:runtime:$composeVersion"
     implementation "androidx.compose.ui:ui:$composeVersion"
     implementation "androidx.compose.ui:ui-tooling:$composeVersion"
     implementation "androidx.compose.foundation:foundation:$composeVersion"
     implementation "androidx.compose.foundation:foundation-layout:$composeVersion"
  7. Add Compose support for Material 3.

     //Material 3 Compose Support
     implementation 'androidx.compose.material3:material3:1.0.0-alpha04'
  8. For simplicity, in …

dimitrilc 30 Junior Poster
Introduction

When working on Android with Kotlin, you might have ran into property delegates on a few occasions, such as activityViewModels() from androidx.fragment.app or remember() from androidx.compose.runtime. In this tutorial, we will learn what property delegates are and how to create them ourselves.

Goals

At the end of the tutorial, you would have learned:

  1. How to create final delegates.
  2. How to create non-final delegates.
Tools Required

A Kotlin IDE such as IntelliJ IDEA version 2021.3.2 (Community Edition).

  1. The Kotlin version used in this tutorial is 1.6.10.
Prerequisite Knowledge
  1. Basic Kotlin.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Kotlin project using Gradle as the build system.

  2. For the Project JDK, use JDK 17.

  3. Add the kotlin-reflect dependency below into your build.gradle.

     implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.10")
Final Property Delegate

In Kotlin, to delegate a property means letting another class provide or set the value for that property. There is a special syntax to use a delegate, which is:

val/var propertyName: Type by expression

In real code, the explicit Type on the variable can be omitted because it can be inferred from the delegate getter.

If the reference is a val, the delegate must provide a getter with the syntax below.

operator fun getValue(thisRef: Any?, property: KProperty<*>): Type

The function signature Type must be of the same or a subtype of the variable declaration. thisRef will contain a reference to the …

dimitrilc 30 Junior Poster
Introduction

If you are working with the latest Material 3 libraries on Android, you might have wondered how to use the Bottom Navigation Bar in your App. In this tutorial, we will learn how to use the Material 3 Bottom Navigation Bar (BNB).

Goals

At the end of the tutorial, you would have learned:

  1. How to use the Material 3 Bottom Navigation Bar.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Intermediate Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the Material 3 dependency below into your project’s build.gradle file.

     implementation 'com.google.android.material:material:1.5.0'
  3. Sync Gradle.

  4. Change the current theme(themes.xml) parent to Theme.Material3.DayNight.NoActionBar.

Add the Bottom Navigation Bar

First, let us add the BNB into our project. I normally use BNB with a LinearLayout-based parent, but that is not a hard requirement. We can just add a BNB to the ConstraintLayout that we already have in activity_main.xml just fine.

  1. Open activity_main.xml in Code view, add the code below under the default <TextView>. The code below already includes constraints for the parent ConstraintLayout, so you do not have to worry about them.

     <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/bottom_navigation_menu" />
  2. Upon seeing the compile error Cannot resolve symbol '@menu/bottom_navigation_menu', go ahead and create the …

dimitrilc 30 Junior Poster
Introduction

Proto DataStore is a great way to store the permanent state of your application, especially if you prefer type safety. In this tutorial, we will learn how to store our App state using the Proto DataStore.

Goals

At the end of the tutorial, you would have learned:

  1. How to store App state using Proto DataStore.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Intermediate Android.
  2. protobuf3. If you are not familiar with protobuf, you can check out the syntax here.
  3. Lifecycle-aware coroutine scopes.
  4. Java Serialization.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Remove the default “Hello World!” TextView.

  3. Add the Gradle dependency below into your project build.gradle.

     implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
     implementation "androidx.datastore:datastore:1.0.0"
     implementation “com.google.protobuf:protobuf-javalite:3.19.3”
  4. Add the Google protobuf plugin to your project build.gradle.

     id "com.google.protobuf" version "0.8.18"
  5. Append the configuration below to the end of your project build.gradle.

     protobuf {
         protoc {
             artifact = 'com.google.protobuf:protoc:3.8.0'
         }
         generateProtoTasks {
             all().each { task ->
                 task.builtins {
                     java {
                         option "lite"
                     }
                 }
             }
         }
     }
  6. If you are not sure what these steps do, the instructions for setting up protobuf in your Android project can also be found in the official docs here.

  7. Add the code below inside of <ConstraintLayout>. This will …

dimitrilc 30 Junior Poster
Introduction

The Storage Access Framework (SAF) provides a great way to access files exposed by other applications via their own DocumentProviders. In this tutorial, we will learn how to use the SAF in our App.

Goals

At the end of the tutorial, you would have learned:

  1. How to open documents via the Storage Access Framework.
Tools Required

Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.

Prerequisite Knowledge
  1. Intermediate Android.
  2. ActivityResult API.
  3. Intents
Project Setup

To follow along with the tutorial, perform the steps below:

  1. You are commended to use a test device or AVD image with the Google Play Store pre-installed.

  2. Install Adobe Acrobat Reader: Edit PDF

  3. Using the default Chrome browser on your AVD, download a sample PDF file from this project’s Github repository here. This ensures that the downloaded file will be written to the MediaStore.Downloads table.

  4. Create a new Android project with the default Empty Activity.

  5. Delete the default “Hello World!” TextView.

  6. Add a new Button to ConstraintView.

  7. Constraint the Button to the center of the screen.

  8. Extract the android:text of the Button to strings.xml with the value of Open PDF.

  9. Your acitivty_main.xml file should now look similar to this.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/open_pdf"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>
Project Overview

Our …

dimitrilc 30 Junior Poster
Introduction

If your App only need a simple way to play videos on Android, then VideoView (android.widget.VideoView) might just fit the bill. In this tutorial, we will learn how to use VideoView for video playback in our App.

Goals

At the end of the tutorial, you would have learned:

  1. How to use VideoView to play videos.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Intermediate Android.
  2. Storage Access Framework (SAF).
  3. ActivityResult APIs.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Remove the default “Hello World!” TextView.

  3. Add a new Button to ConstraintLayout.

  4. Constraint it to the top, left and right of the screen.

  5. Give the Button 64dp margin from the top of the screen.

  6. Extract the Button’s android:text value to strings.xml with the value as Play Video.

  7. Your activity_main.xml should now look similar to this.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:app="http://schemas.android.com/apk/res-auto"
         xmlns:tools="http://schemas.android.com/tools"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         tools:context=".MainActivity">
    
         <Button
             android:id="@+id/button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="64dp"
             android:text="@string/button"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />
    
     </androidx.constraintlayout.widget.ConstraintLayout>
  8. From the test device’s web browser, download the sample video from this project’s Github repository here. The sample video used here is the trailer of the short movie Sintel (© copyright Blender Foundation | durian.blender.org). Optionally, you can also drag and drop the video from your …

dimitrilc 30 Junior Poster
Introduction

In Android development, the MediaStore API is a great API to use if you are building a music player. In this tutorial, we will learn how to query for audio files in the MediaStore database.

Goals

At the end of the tutorial, you would have learned:

  1. How to query for Audio files from the MediaStore.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Intermediate Android
  2. Android permissions.
  3. ActivityResult API.
  4. Basic SQL.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Remove default “Hello World!” TextView.

  3. Add a new Button under ConstraintLayout.

  4. Constraint the Button to the center of the screen.

  5. Extract the Button android:text value to strings.xml. Set the Resource Name to button_startQuery and Resource Value to Start Query.

  6. Your activity_main.xml content should be similar to the code below.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button_startQuery"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  7. Download the .mp3 file called Unexpected Gifts of Spring by Alextree from the FreeMusicArchive. This song is licensed under CC BY 4.0.

Project Overview

The frontend of our project is quite simple. It only has a single button. When the project is completed, our App should be able to perform the workflow …

dimitrilc 30 Junior Poster
Introduction

The release of Android 12 also came together with Material 3. Whether you love it or hate it, it is likely to be here to stay for a couple of years, therefore, it would be useful to know how to use it. In this tutorial, we will learn how to enable Material 3 themes and how to add the Material 3 Top App Bar into our app.

Goals

At the end of the tutorial, you would have learned:

  1. How to subclass a Material 3 theme.
  2. How to add Material 3 Top App Bar.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.
Inherit Material 3 Theme

It is recommended that you apply an app-wide Material 3 theme before using any Material 3 component. Because we wanted to use the Material 3 Top App Bar, we should apply a theme such as Theme.Material3.DayNight.NoActionBar to our App.

  1. In your current project, navigate to res > values > themes.

  2. Open up both themes.xml and themes.xml (night).

  3. Inspect both files. Notice that both of their <style> elements have Theme.MaterialComponents.DayNight.DarkActionBar as the parent. This is only true on my build of Android Studio. If you are using a newer build in the future, you might see a …

dimitrilc 30 Junior Poster
Introduction

WorkManager is the preferred method of managing background tasks on Android. It also includes convenient extensions for RxJava3 and Kotlin Coroutines.

In this tutorial, we will learn how to use WorkManager as well as how to observe the background task with the debugging tool Background Task Inspector.

Goals

At the end of the tutorial, you would have learned:

  1. How to use WorkManager.
  2. How to use the Background Task Inspector.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the WorkManger dependency to your module build.gradle file.

     def work_version = "2.7.1"
    
     implementation "androidx.work:work-runtime-ktx:$work_version"
  3. Remove the default “Hello World!” TextView.

  4. Add a new Button inside ConstraintLayout.

  5. Constrain the Button to the center of ConstraintLayout.

  6. Extract the Button android:text value to strings.xml, using download_button as the resource name and Download File as the Resource value.

Your activity_main.xml should look similar to the code below.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <Button
       android:id="@+id/button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/button_download"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Primary Classes from androidx.work

Before writing any code, we will first need to know about the primary classes of WorkManager. Most of the classes related to WorkManager can be found in the …

dimitrilc 30 Junior Poster
Introduction

Coil is a popular image loading library for Kotlin-first Android projects. It is lightweight, fast and is super easy to use.

In this tutorial, we will learn how to use Coil to load images into our project.

Goals

At the end of the tutorial, you would have learned:

  1. How to load images with Coil.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Remove the Default “Hello World!” TextView.

  3. Add a new ImageView inside ConstraintLayout.

     <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:srcCompat="@tools:sample/avatars" />
  4. Add a new Button below ImageView with the code below.

     <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/load_image"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />
  5. Add the <string> elements below to your strings.xml file.

     <string name="load_image">Random Image</string>
     <string name="circle">https://github.com/dmitrilc/DaniwebAndroidNativeCoil/raw/main/app/src/main/res/drawable/circle.png</string>
     <string name="rect">https://github.com/dmitrilc/DaniwebAndroidNativeCoil/raw/main/app/src/main/res/drawable/rect.png</string>
     <string name="triangle">https://github.com/dmitrilc/DaniwebAndroidNativeCoil/raw/main/app/src/main/res/drawable/triangle.png</string>
  6. The dependency to the Coil library to your module gradle.build file.

     implementation 'io.coil-kt:coil:1.4.0'
  7. Inside your AndroidManifest.xml, Add the android.permission.INTERNET permission before <application>, but after <manifest>.

     <uses-permission android:name="android.permission.INTERNET" />
Coil Packages

The Coil library includes a handful of artifacts. For the two base artifacts, certain variants contain a singleton ImageLoader instance and (Kotlin) extension functions for ImageView.

  1. io.coil-kt:coil-base: base artifact
  2. io.coil-kt:coil: base artifact and include singleton …
dimitrilc 30 Junior Poster
Introduction

In this tutorial, we will learn how to capture a picture with the new ActivityResult APIs(androidx.activity.result), which replace the startActivityForResult() and onActivityResult() APIs(from Activity class).

Additionally, we will also learn about the ContentProvider FileProvider, which we will have to set up to save our images.

Goal

At the end of the tutorial, you would have learned:

  1. How to use ActivityResult APIs to launch another Activity(default Camera) from your App.
  2. How to set up and use FileProvider.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Intermediate Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Remove the default Hello World! TextView.

  3. Add a new Button inside ConstraintView using the code below.

     <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button_takePicture"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  4. Add these string resources in strings.xml. You should modify the authorities string value if your domain name is different.

     <string name="button_takePicture">Take Picture</string>
     <string name="temp_images_dir">temp_images</string>
     <string name="temp_image">temp_image.jpg</string>
     <string name="authorities">com.example.fileprovider</string>
  5. Add a new ImageView under the Button using the code below.

     <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button"
        tools:srcCompat="@tools:sample/avatars" />
  6. Your activity_main.xml should look like the screenshot below.

1.png

Project Overview

Firstly, let us quickly go over the starter project.

  1. The project mostly has only two elements, …
dimitrilc 30 Junior Poster
Introduction

Navigation component is an abstraction on top of FragmentManager, which simplifies navigation between fragments. In this tutorial, we will learn how to use the Navigation component in our App.

Goal

At the end of the tutorial, you would have learned:

  1. How to use the Navigation Component.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Remove the default Hello World! TextView.

  3. Add the two string resources below into strings.xml.

     <string name="hello_blank_fragment">Hello blank fragment</string>
     <string name="second_screen">2nd Screen</string>
  4. Add dependencies below to your Module gradle file.

     def nav_version = "2.3.5"
    
     // Kotlin
     implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
     implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
Navigation Component Concept Overview

Before we can start using the Navigation component, we will need to understand the basic concepts of this library.

  1. Navigation Graph: this is an XML file that contains the navigation logics for your Destinations. Android Studio includes a powerful GUI editor to make it easy to visualize your App’s navigation flow.
    10000201000006280000029B667BE17906B2A331.png
  2. Navigation Host: this is an empty activity that houses a NavHostFragment.
  3. NavHostFragment: an object whose primary job is swapping out destinations using its NavController.
  4. NavController: an object with navigate() functions that you can call to direct user navigation.
  5. Destination: where the user navigates to.
  6. Home Destination: the …
dimitrilc 30 Junior Poster
Introduction

Based on the latest report from Appbrain, Samsung has the highest market share(>35%) among all of the Android OEMs, therefore it is important that your apps are tested against Samsung phones.

In this tutorial, we will learn how to load Samsung Emulator skins onto our emulator devices.

Goals

At the end of the tutorial, you would have learned:

  1. How to load Samsung Emulator skins onto Android emulators with Android Studio.
  2. How custom AVD skins work.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.
AVD Skins

Before learning how to add Samsung Emulator skins to our emulators, first, let us dig a little bit deeper into custom AVD (Android Virtual Device) skins.

For each emulator managed by the AVD Manager, there is an advanced option called Custom skin definition, which allows you to load a skin that is different from stock Android.

10000201000001C10000002F981313D99AB37890.png

You can either get these skins directly from the OEMs or create them yourselves. Unfortunately, not all Android OEMs provide them to developers, but Samsung is one of the few OEMs that provides a collection of pre-made skins for their devices.

Custom Skin only applies to the emulator frame

Even though we can make our stock emulators look and feel like real Samsung …

dimitrilc 30 Junior Poster
Introduction

Regardless of whether you like notches on your devices or not, they exist, so as developers, we will have to adapt our Apps to work with them. In this tutorial, we will learn how to adapt our code to work with notches.

The device used in this example is the Pixel 3 XL because Android Studio already provides an AVD skin for this device.

Goals

At the end of the tutorial, you would have learned:

  1. How to adapt your App to work with display cutouts.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. In Android Studio, open the AVD Manager by going to Tools > AVD Manager.

  3. Select Create Virtual Device from the bottom left.
    10000201000000DB0000002E64217D460D05F7A1.png

  4. In the Phone Category, select the Pixel 3 XL, then hit Next.
    10000201000003EA00000274B5BEBC38EDF36B64.png

  5. Select the Android S Image with API Level 32 (x86 or ARM depends on your PC), and hit Next.
    1.png

  6. On the last screen, verify that Enable Device Frame is checked, and then hit Finish.
    2.png

  7. Create another AVD with the exact same configuration, but without the Device Frame.

  8. I was unable to find any …

dimitrilc 30 Junior Poster
Introduction

In Android development, Espresso can be combined with Junit to create powerful UI automation tests. In this tutorial, we will learn how to create Espresso tests.

Goals

At the end of the tutorial, you would have learned:

  1. How to create Espresso tests.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Replace the content of activity_main.xml with the layout XML below.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <EditText
            android:id="@+id/editText_plaintTextInput"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp"
            android:hint="@string/input_text_here"
            android:inputType="text"
            app:layout_constraintBottom_toTopOf="@id/button_submit"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <Button
            android:id="@+id/button_submit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button"
            app:layout_constraintBottom_toTopOf="@id/textView_finalText"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/editText_plaintTextInput" />
    
        <TextView
            android:id="@+id/textView_finalText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="@string/no_value"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/button_submit" />
    
     </androidx.constraintlayout.widget.ConstraintLayout>
  3. Replace strings.xml with the xml below.

     <resources>
        <string name="app_name">Daniweb Espresso Test</string>
        <string name="button">SUBMIT</string>
        <string name="no_value">No Value</string>
        <string name="input_text_here">Input text here</string>
     </resources>
Getting familiar with the demo app

Even though all of the boilerplate code has been provided for this tutorial, let us quickly go over the state of the demo app right now.

First, launch the app, and we can see that there are three elements in our application:

  1. An EditText element for users to input plain text. It provides a hint of “Input text here” to let the user …
allstarbpo0 commented: Thanks for this info +0
dimitrilc 30 Junior Poster
Introduction

UI Automator is a library that allows you to create tests that can interact with other components besides your App, such as Settings or other Android components. In this tutorial, we will learn how to incorporate UI Automator into our tests.

Goals

At the end of the tutorial, you would have learned:

  1. How to use UI Automator to create tests.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Give the default TextView the android:id of “textView_helloWorld

  3. Add the dependency for Ui Automator to your module gradle file.

     androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
UI Automator Overview

UI Automator simply refers to a group of classes belonging to the androidx.test.uiautomator package. The primary classes that you need to know in this package are:

  1. UiDevice: allows you to access device state and simulate user actions. to simulate user actions, you can use methods such as click(), drag(), pressX(), etc.
  2. UiObject: represents a view. You can obtain UiObject instances via findObject() methods from the UiDevice class. UiObject’s public constructors are deprecated. You can also reuse this object for different views matching the same UiSelector.
  3. UiSelector: a filter object used to create a UiObject. If you are familiar with Hamcrest Matchers, then this is somewhat similar to …
dimitrilc 30 Junior Poster
Introduction

In Android development, Intents are used to request an action from an app component. There are two types of Intents: explicit and implicit.

Explicit Intents are used when you know the exact package name of the component you need started. Implicit Intents, however, do not need a specific component, and depend on the Android system to find all Applications that can handle the Intent.

In this tutorial, we will learn how to create an implicit Intent.

Goals

At the end of the tutorial, you would have learned:

  1. How to create implicit Intents.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 3.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.
  2. Delete the “Hello World!” TextView from activity_main.xml.
  3. Add a new Button to activity_main.xml.
  4. Constraint the Button to all 4 sides of the ConstraintLayout.
  5. Extract the hard-coded android:text attribute of Button to a String resource and also change the value to “Open URL
  6. Add a new EditText element from the Palette > Text > Plain Text.
  7. Constraint the top, left, and right sides of the EditText element to the top, left and right sides of ConstraintLayout, respectively.
  8. Constraint the bottom of the EditText element to the top of the Button.
  9. Change android:inputType of EditText to textUri.
  10. Remove the android:text attribute from EditText.
  11. Change the id of the EditText …
dimitrilc 30 Junior Poster
Introduction

Snackbars are great for displaying brief messages providing feedback to your users.

In this tutorial, we will learn how to create Snackbars.

Goals

At the end of the tutorial, you would have learned:

  1. How to create a Snackbar.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.
  2. Remove the “Hello World!” TextView from acitivity_main.xml.
  3. Add a new Button to activity_main.xml.
  4. Constraint the Button to all 4 sides of the ConstraintLayout.
  5. Extract the hard-coded android:text attribute of Button to a String resource and also change the value to “Load Data”.
  6. Add the attribute android:id="@+id/main" to the ConstraintLayout element in activity_main.xml.

Your activity_main.xml file should look similar to the code below.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity"
   android:id="@+id/main">

   <Button
       android:id="@+id/button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/button"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Snackbars Overview

Snackbar (com.google.android.material.snackbar.Snackbar) is a class in the Material Android library. Snackbars can only be added programmatically.
Snackbars display a brief message at the bottom of the screen and should disappear by themselves without user intervention.

Screenshot_1639071733.png

Snackbars should NOT be intrusive to your users, so you should follow Material Design guidelines here when using Snackbars.

Bind Button to Snackbar

Before getting into making the Snackbar, let us bind the Button …

dimitrilc 30 Junior Poster
Introduction

Notification is a great way to display information outside of your Android application. Your end users most likely already interact with notifications everyday, so knowing how to create notifications can greatly boost your user experience (but can also backfire if used incorrectly).

In this tutorial, we will learn how to create a simple notification for our app.

Goals

At the end of the tutorial, you would have learned:

  1. How to create a notification on Android.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 3.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Delete the “Hello World!” TextView.

  3. Add a new Button inside ConstraintLayout.

  4. Extract Hard-coded “Button” to strings.xml. Change the text value to “Add Notification”.

  5. Constraint the Button to all 4 sides of ConstraintLayout with 0 margin.

  6. Your activity_main.xml should look like this:

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  7. Your strings.xml should look like this:

     <resources>
        <string name="app_name">Daniweb Create Notification</string>
        <string name="button">Add Notification</string>
     </resources>
NotificationChannel

There is quite a bit of boilerplate code required to create just a simple Notification. If you want your notification to work on Android 8.0 and above, the very first …

dimitrilc 30 Junior Poster
Introduction

Starting on API level 25, static Shortcuts can be used to quickly navigate to a specific point in your app. In this tutorial, we will learn how to create static shortcuts for an Android app.

Goals

At the end of the tutorial, you would have learned:

  1. How to create static shortcuts.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 3.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.
Static Shortcuts

Static shortcuts are simply icons that allow your users to quickly go to a specific point in your app. For example, in the animation below, Search, Subscriptions, and Explore are all shortcuts.

shortcut.gif

In supported launchers, shortcuts are shown when the app icon is held down by the end user.

Static shortcuts should only be used to navigate to content that stays the same during the lifetime of your app. There are also two other types of shortcuts, dynamic shortcuts and pinned shortcuts, but they are out of scope for this tutorial.

Creating a static shortcut

The steps to create a static shortcut, at the minimum, include:

  1. Create an XML file to house the shortcut(s) declarations.
  2. Identifying the Intent for your shortcut.
  3. Bind shortcuts to their intents.
  4. Provide the meta-data key-value pair to the application entry point activity in the manifest file.
dimitrilc 30 Junior Poster
Introduction

With OLED screens becoming more and more common on smartphones, adapting a dark theme to your app can provide a boost to battery life.

In this tutorial, we will learn how to add a dark theme to your native Android app.

Goals

At the end of the tutorial, you would have learned:

  1. How to modify the dark theme.
  2. How to switch themes programmatically.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 3.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Extract the Hello World! String resource to the strings.xml file, so res/values/strings.xml should have

     <string name="hello_world">Hello World!</string>
MODE_NIGHT Flags

The first concept that we need to understand are the MODE_NIGHT flags.

Inside of the androidx.appcompat.app.AppCompatDelegate class, there are a few constants prefixed with MODE_NIGHT, with each representing a flag that determines how your app switches to the dark theme.

  1. MODE_NIGHT_AUTO: Deprecated. Replaced by MODE_NIGHT_AUTO_TIME.
  2. MODE_NIGHT_AUTO_BATTERY: Night mode switches on when Battery Saver is enabled.
  3. MODE_NIGHT_AUTO_TIME: Night mode switches on/off depending on the time of day.
  4. MODE_NIGHT_FOLLOW_SYSTEM: Follow the System settings. For example, on my phone (Android 12), this setting can be found at Settings > Display > Appearance > Dark Theme.
  5. MODE_NIGHT_NO: Night mode is switched off.
  6. MODE_NIGHT_UNSPECIFIED: Mostly used with setLocalNightMode() to override the default night mode.
  7. MODE_NIGHT_YES: …
dimitrilc 30 Junior Poster
Introduction

All TextView objects have a special attribute called textAppearance. This attribute can be used to set the style for text content in a TextView without affecting other styling attributes on the same TextView object.

In this tutorial, we will learn how to apply textAppearance to TextView objects.

Goals

At the end of the tutorial, you would have learned:

  1. How to apply textAppearance to TextViews.
Tools Required
  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 3.
Prerequisite Knowledge
  1. Basic Android.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Extract the Hello World! String resource to the strings.xml file, so res/values/strings.xml should have

     <string name="hello_world">Hello World!</string>
Apply textAppearance via XML

There are two ways to apply textAppearance to a TextView. We can apply it via XML or programmatically. To apply textAppearance using XML, we would need to set the android:textAppearance attribute.

  1. Open up activity_main.xml in the Design surface, select the TextView, and search for “textAppearance” in the Attributes panel.

attributes.png

  1. Select the dropdown menu to use the Large value.

large.png

  1. In Code view, the attribute that was added to TextView is:

     android:textAppearance="@style/TextAppearance.AppCompat.Large"

There are a lot more values or even custom styles that you can add to textAppearance that are not in the dropdown menu. …

dimitrilc 30 Junior Poster
Introduction

Complex Views can use up RAM and reduce the performance of your Android apps. If a View is rarely used, you should consider delaying its rendering until it is needed by your app. One way to do that is to use the ViewStub View.

ViewStub is very cheap to inflate, so it can often be used as a stub for complex Views.

Goals

At the end of the tutorial, you would have learned:

  1. How to use ViewStub to lazily load Views.
Prerequisite Knowledge
  1. Basic Android development knowledge.
Tools Required
  1. Android Studio.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.
  2. Delete the “Hello WorldTextView.
ViewStub Concept Overview

ViewStub is cheap to inflate because it is invisible and has zero dimensions. At development time, it references another layout file with the android:layout attribute. The XML file that it references does not necessarily have to be one of the LayoutX classes (FrameLayout, ConstraintLayout, etc). Any View would do.

When a ViewStub is inflated, the underlying layout will inherit ViewStub’s layout parameters and replaces ViewStub in the parent layout. You should be careful when attempting to reference the ViewStub id after inflation because the app will throw a NullPointerException.

There are two common ways to inflate a ViewStub:

  1. Set the visibility to the constant View.VISIBLE. Use setVisibility() method if you are using Java.
  2. Call the inflate() function …
dimitrilc 30 Junior Poster
Introduction

Among all of the animation API’s on Android, MotionLayout is one of the best classes to use to create complex animations. It can animate property and motion of multiple views at the same time. Other benefits include declarative style XML and the intuitive Motion Editor built right into Android Studio.

In this tutorial, we will learn how to move a drawable resource with MotionLayout.

Goals

At the end of the tutorial, you would have learned:

  1. How to set up MotionLayout.
  2. How to use the Motion Editor in Android Studio.
Prerequisite Knowledge
  1. Basic Android development knowledge.
Tools Required
  1. Android Studio.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Delete the “Hello WorldTextView from activity_main.xml.

  3. Right-click on res/drawable -> New -> Vector Asset.

  4. Choose Clip Art as Asset Type.

  5. For the Name, use ic_android_black_100dp.

  6. For the Clip Art, find and select the android clip art.

  7. Set the size to 100x100.

  8. Use the default black color 000000.

  9. Opacity should be set to 100%.

  10. No need to check the TRL option.

  11. Select Next.
    Configure_Vector_Asset.png

  12. The icon path is under the default drawable directory at main/drawable/ic_android_black_100dp.xml
    confirm_icon_path.png

  13. Add a new ImageView below ConstraintView in activity_main.xml, selecting ic_android_black_100dp as the resource. If you do …

dimitrilc 30 Junior Poster
Introduction

If your Android app is available to a global audience, you might have wondered how to load localized Strings into your app. This tutorial will teach you how to do just that.

There are quite a few steps involved, but once you understand the concepts, it will make loading other alternate resources easier for you as well, such as drawables or fonts.

Our end goal is to have the app display “Hola Mundo!”, which is the Spanish equivalent of “Hello World!” when the Android device language setting changes to Spanish.

Goals

At the end of the tutorial, you would have learned:

  1. How to load alternate String resources on Android native.
Prerequisite Knowledge
  1. Basic Android development knowledge.
  2. Basic knowledge of the Java class Locale.
Tools Required
  1. Android Studio.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.
  2. Our project so far is pretty barebone. Android Studio created an empty TextView for us with “Hello World” as its content.

helloworld.png

  1. It is still a hard-coded String, so let us go ahead and use a String resource under res/values/strings.xml instead.

  2. In strings.xml, add a new <string>

    <string name="hello_world">Hello World</string>
  3. In res/layout/activity.xml, modify the TextView attribute android:text to reference the String resource that we just created previously.

     android:text="@string/hello_world"
Resource Directory and Configuration Qualifier

The first thing that we need to do …

dimitrilc 30 Junior Poster
Introduction

One of the best debugging tools in Android Studio is the live database inspector. It allows developers to peek at the current state of the database and even run queries against it.

In this tutorial, we will learn how to use it to debug a local SQLite database.

Goals

At the end of the tutorial, you would have learned:

  1. How to create a SQLite Database.
  2. How to debug a SQLite Database using the Database Inspector.
Prerequisite Knowledge
  1. Basic Android development knowledge.
  2. Basic SQL knowledge.
Tools Required
  1. Android Studio.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.
Creating the SQLite database

Before learning about the database inspector, we obviously must have a database to inspect. So we will need to create the database first.

The first thing that we need to do to create a SQLite database is to define a contract. A contract is a class that specifies your schema layout. We do not need to instantiate this contract, so Android prefers for developers to put these contracts inside a Kotlin Object (Singleton).

The database used in this tutorial comprises exactly one table called student. The student table would have two columns, name and age. Let us create a Kotlin Object called StudentContract from the code below (you will need to a create new Kotlin file).

package com.example.daniwedblivedebug

object StudentContract {
}

Next, we need to add …

dimitrilc 30 Junior Poster
Introduction

When localizing your application, ResourceBundle can be a great way to load Strings from different locales.

The only concrete implementation of ResourceBundle is PropertyResourceBundle, which allows us to load static Strings from a text file. Because .properties is a common file extension used for ResourceBundles, creating ResourceBundle objects using this file format would be the focus of this tutorial.

Property files are commonly used to load application properties for Spring Boot projects, so if you have been working with Spring Boot, then this file format should be familiar to you; the only difference to what we create today is that we will be using property files to load localization data for the ResourceBundle object, so there are some rules that we must follow.

Goals

At the end of the tutorial, you would have learned:

  1. How to create ResourceBundle objects from property files.
Prerequisite Knowledge
  1. Basic Java.
  2. Basic understanding of the java.util.Locale class.
Tools Required
  1. A Java IDE such as IntelliJ Community Edition.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Java project.
  2. Create a package com.example.
  3. Create a class called Entry.
  4. Create the main() method inside Entry.java.
Concept: Family

When working with ResourceBundle, the most basic concept that developers need to understand is “family”. A family is technically just a group of property files with a common base (file) name. For example, all of the property files in the list below belong to the …

dimitrilc 30 Junior Poster
Introduction

Junit is a popular framework to create tests for Java applications. Although individual unit tests are mostly straightforward, integration and functional tests are a little bit more involved because they usually require multiple components working together. For that reason, understanding the life cycle of Junit tests can be greatly beneficial.

In this tutorial, we will learn about the lifecycle of Junit 5 test instances.

Goals

At the end of the tutorial, you would have learned:

  1. Different stages of a Junit instance lifecycle.
Prerequisite Knowledge
  1. Basic Java.
  2. Basic Junit.
Tools Required
  1. A Java IDE such as IntelliJ Community Edition.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Gradle Java project. I am using Java 17 and Gradle 7.2, but any Java version 8+ should work.
  2. Add the dependency for unit-jupiter-engine. The latest version is 5.8.1 as of this writing.

Below is the content of my build.gradle file.

plugins {
   id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
   mavenCentral()
}

dependencies {
   testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
   testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}

test {
   useJUnitPlatform()
}
  1. Under src/test/java, create a new package called com.example.
  2. Under com.example, create a new class called LifecycleTest.
  3. We do not need the main() method for this tutorial.
Lifecycle Overview

There are 5 stages of a Junit 5 test instance lifecycle. The list below orders them from first to last. The @ symbol means that there is an annotation matching that lifecycle stage as …

dimitrilc 30 Junior Poster
Introduction

When collecting a stream, the groupingBy() Collector(java.util.stream.Collector) can be used to put data into different groups based on specified conditions. groupingBy() is a little bit more complicated to use than partitioningBy() because the key can be of any type, whereas with partitioningBy(), the keys must be of type Boolean.

There are 3 overloaded variants of the groupingBy() method in the Collectors(java.util.stream.Collectors) class. In this tutorial, we are going to learn how to use all of these 3 variants of groupingBy().

Goals

At the end of the tutorial, you would have learned:

  1. How to use the groupingBy() Collector.
Prerequisite Knowledge
  1. Intermediate Java.
  2. Java Streams(java.util.stream.Stream).
Tools Required
  1. A Java IDE.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Java project.

  2. Create a package com.example.

  3. Create a class called Entry.

  4. Create the main() method inside Entry.java.

  5. Our examples will use Cake objects with different properties to demonstrate groupingBy(), so declare the package-private top-level record class Cake inside Entry.java from the code below.

     record Cake(Shape shape, Color color, BigDecimal price){}
  6. Then create an enum called Shape.

     enum Shape { TRIANGLE, CIRCLE, DIAMOND }
  7. We do not have to create our own class to encapsulate colors, we will use the premade Java class java.awt.Color.

groupingBy() Concept Overview

In main(), add the List of cakes using the code below:

var cakes = List.of( …
dimitrilc 30 Junior Poster
Introduction

Whenever we want to modify numbers from multiple threads running concurrently, such as modifying a counter, one of the best options is to use the AtomicX classes. The most basic numeric atomic classes are AtomicInteger and AtomicLong.

In this tutorial, we will learn how to use the AtomicInteger class to atomically update numbers as well as some of its common operators.

Goals

At the end of the tutorial, you would have learned:

  1. How to use AtomicInteger to make numerics thread-safe.
Prerequisite Knowledge
  1. Intermediate Java.
  2. Basic Java Concurrency.
Tools Required
  1. A Java IDE such as IntelliJ Community Edition.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Java project.
  2. Create a package com.example.
  3. Create a class called Entry.
  4. Create the main() method inside Entry.java.
Concept Overview

Before learning how to use AtomicInteger, let us take a look at what problem it solves. First, we will try to modify a regular int from multiple threads, so create a static counterproperty for the Entry class.

static int counter;

And then add the method called unsafe() from the code below into the Entry class as well.

private static void unsafe(){
   ExecutorService executorService = Executors.newCachedThreadPool();

   for (int i = 0; i < 5; i++){
       executorService.submit(() -> {
           System.out.println(counter++);
       });
   }

   try {
       executorService.awaitTermination(1, TimeUnit.SECONDS);
   } catch (InterruptedException e) {
       e.printStackTrace();
   } finally {
       executorService.shutdown();
   }
}

We chose the name unsafe() to signify that modifying an int from multiple threads …