osman

Testing ViewModel

ViewModel is a part of the Android Jetpack library and is designed to store and manage UI-related data in lifecycle-conscious way. It allows data to survive configuration changes such as screen rotation and it also separates the UI data from the Activity or Fragment, making it easier to test and maintain.
ViewModel can also communicate with a repository to get data from different sources like a database or remote API. In this case ViewModel is not responsible for handling data storage and it just receives the data from repository and it is used to update the UI.

There are several ways to test a ViewModel. If the ViewModel doesn’t get data from the repository, we can test it directly by creating ViewModel instance in the test class and than write the test methods.

If the ViewModel gets data from the repository, we need to take care of the repository class first, and then work with the ViewModel. For this purpose we will create a fake repository and pass it to the ViewModel:

    @Before
    fun Setup() {
        fal fakeRepository = FakeTestRepository()
        val viewModel = MyViewModel(fakeRepository)

FakeRepository extends BaseRepository interface (same as in DefaultRepository that we use in main program) and create temporary list that we will use for testing instead of the Room database.


class FakeTestRepository: BaseRepository {

    private val imageItemList = mutableListOf<ImageDataModel>()

    override var allImagesFromDao: Flow<List<ImageDataModel>> = flow { emit(imageItemList) }

    override suspend fun insert(imageDataModel: ImageDataModel): Long {
        imageItemList.add(imageDataModel)
        return 1    }

    override suspend fun update(imageDataModel: ImageDataModel): Int {
        imageItemList.add(imageDataModel)
        return 1    }

    override suspend fun delete(imageDataModel: ImageDataModel): Int {
        imageItemList.remove(imageDataModel)
        return 1    }

    override suspend fun deleteAll(): Int {
        imageItemList.removeAll(imageItemList)
        return 1    }
}

In the test folder we will create MyViewModelTest class and annotated it with @RunWith(MockitoJUnitRunner::class). After that we need to define InstantTaskExecutorRule which swaps the background executor used by the Architecture Components with a different one which executes each task synchronously.

@RunWith(MockitoJUnitRunner::class)
class MyViewModelTest {

    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()

    @OptIn(ExperimentalCoroutinesApi::class)
    @get:Rule
    var mainCoroutineRule = MainCoroutineRule()

The second rule is MainCoroutineRule() which we use to test coroutines. Since coroutines can be asynchronous and run across multiple threads, we meed to use TestDispachers to run the test on a single test thread if new coroutines are created during the test.

@ExperimentalCoroutinesApi
class MainCoroutineRule (
    private val dispatcher: CoroutineDispatcher = TestCoroutineDispatcher()
    ) : TestWatcher(), TestCoroutineScope by TestCoroutineScope(dispatcher) {

        override fun starting(description: Description?) {
            super.starting(description)
            Dispatchers.setMain(dispatcher)
        }

        override fun finished(description: Description?) {
            super.finished(description)
            cleanupTestCoroutines()
            Dispatchers.resetMain()
        }
}

Our ViewModel uses LiveData, and to be able to access that data we need to use Observer and CountDownLatch in our code:

    @Test
    fun `insert data, check if data are inserted and return success`(){

        val  img1 = ImageDataModel(id = 1, "http://www.example.com/img1.png", "image1")
        viewModel.insert(img1)

        val latch = CountDownLatch(1)
        val observer = Observer<List<ImageDataModel>> {
                assertEquals(img1, it[0])
                latch.countDown()
            }
        viewModel.getSavedImages().observeForever(observer)

        if (!latch.await(2, TimeUnit.SECONDS)) {
            throw TimeoutException("LiveData value was never set.")
        }
    }

The CountDownLatch counter is initialized with a number of threads. The counter is decremented each time a thread completes its execution. When the count reaches zero, it means that all threads have completed their execution, and the main thread waiting on the latch resumes the execution. That way we can observe our livedata and get necessary information for test. To avoid duplication code in each test, we can use a generic function that we can call from our test:

    /* Copyright 2019 Google LLC.
   SPDX-License-Identifier: Apache-2.0 */
    fun <T> LiveData<T>.getOrAwaitValue(
        time: Long = 2,
        timeUnit: TimeUnit = TimeUnit.SECONDS
    ): T {
        var data: T? = null
        val latch = CountDownLatch(1)
        val observer = object : Observer<T> {
            override fun onChanged(o: T?) {
                data = o
                latch.countDown()
                this@getOrAwaitValue.removeObserver(this)
            }
        }
        this.observeForever(observer)

        // Don't wait indefinitely if the LiveData is not set.
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }
        @Suppress("UNCHECKED_CAST")
        return data as T
    }

And now the test functions can look like this:

    @Test
    fun `insert data, get list from livedata, return success if equal`() {

        val  img1 = ImageDataModel(id = 1, "http://www.example.com/img1.png", "image1")
        viewModel.insert(img1)

        assertEquals(viewModel.getSavedImages().getOrAwaitValue()[0].title, "image1")
        assertEquals(viewModel.getSavedImages().getOrAwaitValue()[0], img1)
    }

And for validating input in our ViewModel we can do:

    @Test
    fun `verify user input, empty value in title input field, return false`() {
        val result = viewModel.validateInput(
            "",
            "http://www.example.com/image2.jpg"
        )
        assertThat(result).isFalse()
    }

Test function for deleting item:

    @Test
    fun `delete item, return true`() {
        val item = ImageDataModel(id = 1, "www.example.com/image2.jpg", "image2")
        viewModel.insert(item)
        viewModel.deleteItem(item)
        assertThat(viewModel.stmessage.value).isEqualTo("1 Row deleted successfully")
    }

The rest of the code you can find here

Testing Room database (with Co-routines and Flow)

Room is a persistence library for Android that provides an abstraction layer over the SQLite database. With the help of the Room, we can easily create the database and perform CRUD operations.
For observing changes to the database and for receiving updates in a real-time Room can utilize Flow, an asynchronous data stream that sequentially emits data.
The basic components of Room are Entity, Dao and Database classes and for this testing we will use the Dao class because it contains all the necessary functions to access data in our database:

@Dao
interface ImageDao {

    @Query("SELECT * FROM image_data_table")
    fun getAllData(): Flow<List<ImageDataModel>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertData(imageDataModel: ImageDataModel): Long

    @Update
    suspend fun updateData(imageDataModel: ImageDataModel): Int

    @Delete
    suspend fun deleteData(imageDataModel: ImageDataModel): Int

    @Query("DELETE FROM image_data_table")
    suspend fun deleteAll(): Int

}

The recommended approach for testing database implementation is writing a JUnit test that runs on an Android device. Because these tests don’t require creating an activity, they should be faster to execute than UI tests.

In the setup() function we have to create new instances of Database for testing purpose. For creating a new instance of database we have to use Room.inMemoryDatabaseBuilder() instead of Room.databaseBuilder() we normally use. inMemoryDatabaseBuilder() creates an in memory version of database. Information stored in an in memory database disappears when the process is killed.

    @Before
    fun setup() {
        database = Room.inMemoryDatabaseBuilder(
            ApplicationProvider.getApplicationContext(),
            ImageDataBase::class.java)
            .allowMainThreadQueries()
            .build()
        dao = database.imageDao()
    } 

For accessing data in the Flow we have to stop the thread. By using a CountDownLatch we can cause a thread to block until other threads have completed a given task. A CountDownLatch is initialized with a given count value. We call the await() method of CountDownLatch to wait till the counter reaches 0, and then we execute our normal code flow. getAllData() method in Dao returns a Flow of a list of objects and the collect method is then used to observe the Flow and receive updates.

    private fun getDataFromFlow() = runBlocking {
        val latch = CountDownLatch(1)
        val imageList = mutableListOf<ImageDataModel>()
        val job = launch(Dispatchers.IO) {
            dao.getAllData().collect { items ->
                imageList.addAll(items)
                latch.countDown()
            }
        }
        withContext(Dispatchers.IO) {
            latch.await(2, TimeUnit.SECONDS)
        }
        job.cancelAndJoin()
        return@runBlocking imageList
    }

And after that we can use return data for testing individual functions in Dao. For example:

    @Test
    fun update_Image_In_Db_should_contain_new_data() = runBlocking {
        val imageItem = ImageDataModel(id = 1, "url1", "image1")
        dao.insertData(imageItem)
        val updatedImageItem = ImageDataModel(id = 1, "url2", "image2")
        dao.updateData(updatedImageItem)

        val imageList = getDataFromFlow()
        assertTrue(imageList.contains(updatedImageItem))
    }

or directly assert data inside .collect block of the code:

    @Test
    @Throws(Exception::class)
    fun insert_Image_In_Db_Return_True() = runTest {
        val imageItem = ImageDataModel(id = 1, "url1", "image1")
        dao.insertData(imageItem)
        val latch = CountDownLatch(1)
        val job = launch(Dispatchers.IO) {
            dao.getAllData().collect { items ->
                assertTrue(items.contains(imageItem))
                latch.countDown()
            }
        }
        latch.await(2, TimeUnit.SECONDS)
        job.cancel()
    }

The complete code you can get here.

Fractals – Mandelbrot set

Fractals are infinitely complex, never-ending patterns that are self-similar across different scales. They are created by repeating a simple process over and over in an ongoing feedback loop.
Nature is full of fractals. For example, trees are natural fractals, patterns that repeat smaller and smaller copies of themselves to create the biodiversity of a forest. Fern is another example of fractal in nature. One really good example of fractal form in nature is Romanesco broccoli, showing self-similar form approximating a natural fractal:


© Wikipedia 2021

The Mandelbrot Set is an Abstract Fractal which can be generated by a computer calculating a simple equation over and over. It is a structure with an infinite amount of details. It is possible to zoom in on the edge of the fractal forever, and it will continue to reveal ever-smaller details.


© Wikipedia 2007

The Mandelbrot set is calculated by equation: f(z) = z2 + c, where c is complex number: c = x + iy In this function i is a square root of -1 and x and y are the horizontal and vertical position whose color we calculate. The function is applied many times, and the output value of z from each iteration is being used as input for the next iteration. During iteration, if the value of z exceeds 2, then iteration halts and the value of c determines if it is located outside the border of the Mandelbrot set (in blue zone) or it lies inside the border of the Mandelbrot set.

To implement the function in our program we have to take in account the x and y coordinate values of the display. Now our fractal function looks like this:

fun calculateMandelbrot(cX: Double, cY: Double): Int {
    var zx = 0.0
    var zy = 0.0
    var i = 0
    while (i < iter && (zx * zx + zy * zy) < 4) {
        val zxtmp = zx * zx - zy * zy + cX + positionX
        zy = 2 * zx * zy + cY + positionY
        zx = zxtmp
        i++
    }
    return i
}

This function has to be called for every dot on the screen and we do this with two while loops. At the same time, for each of those points we add the color values to the IntArray variable mImage:

while (y < mHeight) {
   val cY = -1.0 + y * (2.0/mHeight) / zoom
   var x = 0
   while (x < mWidth) {
      val cX = -2.5 + x * (3.5/mWidth) / zoom

     val colorIndex = calculateMandelbrot(cX, cY) * (mPalette.size - 1) / iter
     mImage[y * mWidth + x] = mPalette[colorIndex]
 
     x++
  }
  y++
}

The variable iter is number of iteration we do. A higher value increase the complexity of the Mandelbrot set, but also increase the time required to calculate and render the final image.

And finally, with the collected information we can create an image with the createImage() function in the SurfView class:

private fun createImage() {
   val can = holder.lockCanvas()
   val bitmap = Bitmap.createBitmap(mImage!!, mWidth, mHeight, Bitmap.Config.ARGB_8888)
   can.drawBitmap(bitmap, 0f, 0f, null)
   holder.unlockCanvasAndPost(can)
}

And here is a full code in Kotlin.

Django installation guide

Django is a Python web framework for full-stack web application development and server development that enables rapid development of secure and maintainable websites.  It is the most used framework for web development in Python programming language. As an open source project that supports the implementation of the most popular packages and Python tools, Django is a perfectly suitable choice for a mobile application backend.
Many global companies are using Django, like Instagram, National Geographic, Mozilla, Spotify, Pinterest, Disqus, Bitbucket, Eventbrite.

https://www.djangoproject.com

Installation

Download and Install Python from: https://www.python.org/downloads/

After that you can install Pipenv, a packaging tool for Python that simplifies dependency management. (https://realpython.com/pipenv-guide and https://pipenv.pypa.io)

In terminal/cmd window type: pip install pipenv

Download and Install VSC – Visual Studio Code editor: https://code.visualstudio.com
After installation, run VSC and in Extensions panel search for Python and install (if already isn’t installed): Python IntelliSense extension.

After that run terminal/cmd, create folder called storefront , enter the folder and type inside that folder:
pipenv install django

Open folder storefront in VSC (or drag&drop iside VSC from explorer)

In terminal/cmd type: pipenv shell
and after that type: django-admin startproject storefront . to start a new project.

To start development server, type in cmd: python manage.py runserver
Default port number is 8000. I you want to use other port number: python manage.py runserver 8800
Now we can enter address and port number we get as a result in the browser, for example: http://127.0.0.1:8000

In VSC open View menu end select Command Palette…
In search box type python interpreter and select Python: Select Interpreter
Click on Enter interpreter path …
Now type in terminal/cmd: pipenv --venv and copy the path into Enter interpreter path… field.
At the end of the path append: \Scripts\python (/bin/python if you are in Linux enviroment)
(something like, for example: C:\Users\G2.virtualenvs\storefront-QPOIJ0Ev\Scripts\python)

Now we can go to View menu in VSC and select Terminal and in terminal we can type: python manage.py runserver
(If you got error that running scripts is disabled on this system, you can try to open new terminal window clicking on + button in terminal window and type again python manage.py runserver. – defaultProfile is known not to work on the initial restored terminal. The same solution is if you sometime got error SyntaxError: invalid syntax)

second solution:
PowerShell by default restricts running scripts unless we change the execution policies. 
In VSC open View menu, select Command Palette and search settings.json. 
Select Open Workspace Settings(JSON) and add following lines:
{
    "terminal.integrated.profiles.windows": {
        "PowerShell": {
          "source": "PowerShell",
          "icon": "terminal-powershell",
          "args": ["-ExecutionPolicy", "Bypass"]
        }
      },
      "terminal.integrated.defaultProfile.windows": "PowerShell",
}
After that we need to restart VS Code and the Terminal. 

For more info go to: https://docs.djangoproject.com/en/4.1/intro/install/ and https://www.youtube.com/watch?v=rHux0gMZ3Eg

Glide library in Kotlin

Glide is fast and efficient image loading library for android. Using Glide library we can download, decode and display images, videos or animated GIFs, resize downloaded images, show placeholder while loading image, show pre-defined images when there is any error while downloading image, apply animation while loading image and use cache for offline viewing.

First, in app/build.gradle file we have to add:

implementation 'com.github.bumptech.glide:glide:4.12.0'

And in AndroidManifest.xml file we need to add internet permission:

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

For loading pictures into ImageView we have to create ImageView element in xml Layout with ID:

<ImageView
  android:id="@+id/imageView" 
  android:layout_width="200dp"
  android:layout_height="200dp"/>

And in MainActivity.kt we can use basic code:

Glide.with(this)
   .load("Your_Image_URL")
   .into(imageView)

Methods that we can use with Glide are:

  • .placeholder("Placeholder Image URL") – to show placeholder while image is being loaded.
  • .error(“Error_Image_URL”) – to show error placeholder.
  • .fallback(“Fallback_Image_URL”) – if requested url/model is null we use fallback placeholder.
  • .override(150, 150) – If we want to resize downloaded image.
  • .centerCrop(), . fitCenter(), .circleCrop() – several in-built transformations.

We can apply transition from a placeholder to newly loaded image, or from a thumbnail to full size image easly by using many transition available to glide. For example:

val factory = DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build()
Glide.with(this)
  .load("Your_Image_URL")
  .transition(withCrossFade(factory))
  .into(imageView)

We cannot do the cross fade between two different images that are loaded with different requests. Glide by default will cancel any existing requests when you start a new load into an existing View or Target. As a result, if you want to load two different images and cross fade between them, you cannot do so with Glide directly. Instead, the easiest way to cross fade across two different images loaded in two separate requests is to use ViewSwitcher containing two ImageViews. Load the first image into the result of getNextView(). Then load the second image into the next result of getNextView() and use a RequestListener to call showNext() when the second image load finishes.

see Glide documentation for more info

Caching in Glide:

When loading image, Glide first checks if image already exist in memory, in case that this image is displayed in another View right now and to see if this image is recently loaded and is it still in memory?
After that Glide check if image is already stored in disk to see if this image has been decoded, transformed, and written to the disk cache before.
If image is neither found in memory nor in disk, Glide sends request to fetch image from remote url.

Using diskCacheStrategy method, we apply disk caching. Value passed in method defines how disk caching is configured:

  • diskCacheStrategy.ALL caches remote data with both DATA and RESOURCE, and local data with RESOURCE only.
  • diskCacheStrategy.AUTOMATIC is set by default and it tries to choose strategy using DataFetcher and EncodeStrategy.
  • diskCacheStrategy.DATA writes retrieved data directly to disk cache before it is decoded.
  • diskCacheStrategy.NONE doesn’t save data to cache and
  • diskCacheStrategy.RESOURCE writes data to disk after it is decoded.

For example, we can apply disk caching for remote data with both DATA and RESOURCE:

Glide.with(CONTEXT)
  .load(URL)
  .diskCacheStrategy(DiskCacheStrategy.ALL)
  .into(imageView)

If we want to load data only from the cache, we use: .onlyRetrieveFromCache(true)

If we want to skip memory or disk caching (or both) we can use: .diskCacheStrategy(DiskCacheStrategy.NONE) or .skipMemoryCache(true)

End now our code for loading picture into ImageView can look something like this:

val imageUrl = "https://glideapps.com/images/cover-logo.png"
val factory = DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build()

Glide.with(this)
  .load(imageUrl)
  .placeholder(R.mipmap.ic_launcher)
  .error(R.drawable.ic_error)
  .fallback(android.R.drawable.ic_menu_slideshow)
  .circleCrop()
  .override(150, 150) // dimensions are in pixels
  .transition(withCrossFade(factory))
  .diskCacheStrategy(DiskCacheStrategy.ALL)
  .into(imageView)

viewBinding

View Binding is a part of Android Jetpack and allows us to more easily write code that interacts with views. Once view binding is enabled in a module, it generates a binding class for each XML layout file present in that module.

Each binding class contains references to the root view and all views that have an ID. The name of the binding class is generated by converting the name of the XML file to Pascal case and adding the word “Binding” to the end. For example, if a layout file is called activity_main.xml the genereted binding class will be called ActivityMainBinding

Instead using findViewById() method we can use viewBinding to access elements in Kotlin .xml file.

First we have to add viewBinding true in app/build.gradle file:

android {
  // …
   buildFeatures {
       viewBinding = true
   }
 }

To set up an instance of the binding class for use with an activity, in MainActivity.kt onCreate() method we have to write code:

class MainActivity : AppCompatActivity() {

   private lateinit var binding: ActivityMainBinding

   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)

         binding = ActivityMainBinding.inflate(layoutInflater)
         val view = binding.root

      setContentView(view)
   }
 }

We can now use the instance of the binding class to reference any of the views, for example:

binding.tv_title.text = "Title"
binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }
binding.imageButton.visibility = View.VISIBLE

View binding has important advantages over using findViewById:
Since view binding creates direct references to views, there’s no risk of a null pointer exception due to an invalid view ID. Additionally, when a view is only present in some configurations of a layout, the field containing its reference in the binding class is marked with @Nullable.
The fields in each binding class have types matching the views they reference in the XML file. This means that there’s no risk of a class cast exception.
Incompatibilities between layout and code will result in build failing at compile time rather than at runtime.

Espresso – UI Testing Framework for Android

Testing your app is an integral part of the app development process. By running tests against your app consistently, you can verify your app’s correctness, functional behavior, and usability before you release it publicly.

Espresso is a testing Framework for writing automated UI – user interface tests for your Android application. With espresso, we can simulate the user interaction in our application, but instead a real user is using the app, with espresso we will automate the process by writing code in Java or Kotlin language.
It has been developed by Google and it allows both black-box testing as well as testing of individual components during development cycles.
Automated testing is an integral part of the development lifecycle.

The main components of Espresso are:

  • Espresso – Entry point to interactions with views (via onView() and onData()).
  • ViewMatchers – A collection of objects that implement the Matcher<? super View> interface. You can pass one or more of these to the onView() method to locate a view within the current view hierarchy.
  • ViewActions – A collection of ViewAction objects that can be passed to the ViewInteraction.perform() method, such as click().
  • ViewAssertions – A collection of ViewAssertion objects that can be passed the ViewInteraction.check() method. Most of the time, you will use the matches assertion, which uses a View matcher to assert the state of the currently selected view.
onView(withId(R.id.my_view))        // withId(R.id.my_view) is a ViewMatcher
    .perform(click())               // click() is a ViewAction
    .check(matches(isDisplayed()))  // matches(isDisplayed()) is a ViewAssertion

In Espresso equivalent to findViewById() is onView(withId(R.id.my_view)). If you want to narrow down your search, combination matches can be used: onView(allOf(withId(R.id.my_view), withText("Hello!"))) or onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
If the target view is inside an AdapterView, such as ListView, GridView, or Spinner, the onView() method might not work. In these cases, you should use onData() instead.

When we have found a suitable matcher for the target view, it is possible to perform instances of ViewAction on it using the perform method.

For example, to click on the view we can use: onView(...).perform(click()) or if we want to execute more than one action with one perform call: onView(...).perform(typeText("Hello"), click())

If the view we are working with is located inside a ScrollView (vertical or horizontal), to ensures that the view is displayed before proceeding to the other action we have to use: scrollTo(): onView(...).perform(scrollTo(), click())

Assertions can be applied to the currently selected view with the check() method. The most used assertion is the matches() assertion. It uses a ViewMatcher object to assert the state of the currently selected view.

For example, to check that a view has the text "Hello!" use: onView(...).check(matches(withText("Hello!")))

If you want to assert that a view with the text "Hello!" is visible – for example after a change of the views visibility flag you can use:
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))

Example Click on the button test:
onView(withId(R.id.button_simple)).perform(click())

To verify the TextView text you can use:
onView(withId(R.id.text_simple))
.check(matches(withText("Hello Espresso!")))

AdapterView is a special type of widget that loads its data dynamically from an Adapter.
The most common example of an AdapterView is ListView. As opposed to static widgets like LinearLayout, only a subset of the AdapterView children may be loaded into the current view hierarchy. A simple onView() search would not find views that are not currently loaded.

Espresso handles this by providing a separate onData() entry point which is able to first load the adapter item in question, bringing it into focus prior to operating on it or any of its children.

In example adapter test we have to open the Spinner, select a specific item, and verify that the TextView contains the item. As the Spinner class is based onAdapterView, it is recommended to use onData() instead of onView() for matching the item:
Open the item selection:
onView(withId(R.id.spinner_simple)).perform(click())
Select an item:
onData(allOf(`is`(instanceOf(String::class.java)), `is`("Americano"))).perform(click())
Verify that text is correct:
onView(withId(R.id.spinnertext_simple))
.check(matches(withText(containsString( "Americano"))))

Espresso logs all view actions to logcat. 
Espresso warns users about presence of AdapterView widgets. When an onView() operation throws a NoMatchingViewException and AdapterView widgets are present in the view hierarchy, the most common solution is to use onData(). The exception message will include a warning with a list of the adapter views. You may use this information to invoke onData() to load the target view.

Espresso setup in Android Studio

Add the following lines inside dependencies of your app’s build.gradle file:

androidTestImplementation('androidx.test.espresso:espresso-core:3.4.0')
androidTestImplementation('androidx.test:runner:1.4.0')
androidTestImplementation('androidx.test:rules:1.4.0')

In the same build.gradle add:

defaultConfig {
   ...
   testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

Android Studio creates tests by default in src/androidTest/java/com.example.package/

To create a test configuration in Android Studio, complete the following steps:

  1. Open Run > Edit Configurations.
  2. Add a new Android Tests configuration.
  3. Choose a module.
  4. Add a specific instrumentation runner: androidx.test.runner.AndroidJUnitRunner
  5. Run the newly created configuration.

The Espresso Cheat Sheet contains most available instances of MatcherViewAction, and ViewAssertion:

For more detailed information releted to app testing you can visit: https://developer.android.com/training/testing

Akeeba Backup for WordPress

The plugin Akeeba Backup, provides backup for your entire WordPress site, including the database.
Akeeba Backup can’t be installed by the plugin manager in WordPress, it must be downloaded and installed manually.

  • To do it go to the Akeeba Kickstart download page and download the WordPress plugin for AkeebaBackup.
  • Go to your Your WordPress Admin area -> Plugins
  • Click Add New
  • Click Upload
  • Choose the file you downloaded
  • Click Install Now
  • Activate the plugin.

In your WordPress control panel you can now see Akeeba Backup.

  • Click it to enter Akeeba Backup.
  • If you first time use Akeeba Backup start Configure Wizard
  • Run the backup
  • Give the backup file a name, and click “Backup Now”.
  • Wait until the backup is finished
  • Manage Backups

You can restore your backups on any server, even a different one than the one you took your backup on. You will need to download Akeeba Kickstart Core to extract the backup archives.

Firebase in a nutshell

To use Firebase in your Android app, you need to add/register your app with your Firebase project:

  1. Open the Firebase console and in the project overview page, click the Android icon or Add app.
  2. Enter your app’s package name in the Android package name field. Make sure to enter the package name that your app is actually using. The package name value is case-sensitive, and it cannot be changed for this Firebase Android app after it’s registered with your Firebase project.
  3. Enter other app information: App nickname and Debug signing certificate SHA-1.
  4. Click Register app.

Add the Firebase Android configuration file to your app:

Click Download google-services.json to obtain your Firebase Android config file (google-services.json) and then move your config file into the module (app-level) directory of your app.

In your (project-level) Gradle file (build.gradle), add rules to include the Google Services Gradle plugin:

buildscript {
  repositories {
    google()  // Google's Maven repository
  }
  dependencies {
    classpath 'com.google.gms:google-services:4.3.10'  // Google Services plugin
  }
}
allprojects {
    repositories {    
    google()  // Google's Maven repository    
  }
}

Declare the dependencies in app/build.gradle file:

apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'  // Google Services plugin

dependencies {
  implementation platform('com.google.firebase:firebase-bom:30.1.0')
  implementation 'com.google.firebase:firebase-analytics-ktx'
  implementation 'com.google.firebase:firebase-auth-ktx'
  implementation 'com.google.firebase:firebase-firestore-ktx'
}

Add Firebase using the Firebase Assistant

The Firebase Assistant registers your app with a Firebase project and adds the necessary Firebase files, plugins, and dependencies to your Android project — all from within Android Studio!

  1. Open your Android project in Android Studio
  2. Open the Firebase Assistant: Tools > Firebase.
  3. In the Assistant pane, choose a Firebase product to add to your app. Expand its section, then click the tutorial link (for example, Analytics > Log an Analytics event).
    1. Click Connect to Firebase to connect your Android project with Firebase.
    2. Click the button to add a desired Firebase product (for example, Add Analytics to your app).
  4. Sync your app to ensure that all dependencies have the necessary versions.
  5. In the Assistant pane, follow the remaining setup instructions for your selected Firebase product.
  6. Add as many other Firebase products as you’d like via the Firebase Assistant!

To get a reference to a database, you must pass the database URL to getInstance(), or for Kotlin+KTXdatabase()

Depending on the location of the database, the database URL will be in one of the following forms:

  • https://DATABASE_NAME.firebaseio.com (for databases in us-central1)
  • https://DATABASE_NAME.REGION.firebasedatabase.app (for databases in all other locations)

Write to database:

// Write a message to the database
val database = Firebase.database
val myRef = database.getReference("message")

myRef.setValue("Hello, World!")

data class User(val username: String? = null, val email: String? = null) {
}

val database = Firebase.database
val databaseRef = database.getReference("UserData")

fun writeNewUser(userId: String, name: String, email: String) {
    val user = User(name, email)

   //to add new user:
     myRef.child("users").child(userId).setValue(user)
  // to update user name:
     myRef.child("users").child(userId).child("username").setValue(name)
}

Read from database:

To make your app data update in realtime, you should add a ValueEventListener to the reference you just created.

The onDataChange() method in this class is triggered once when the listener is attached and again every time the data changes, including the children.

// Read from the database
private lateinit var databaseRef: DatabaseReference

databaseRef = Firebase.database.getReference("UserData")
databaseRef.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        // This method is called once with the initial value and again
        // whenever data at this location is updated.
        val value = dataSnapshot.getValue<String>()
        Log.d(TAG, "Value is: $value")
    }

    override fun onCancelled(error: DatabaseError) {
        // Failed to read value
        Log.w(TAG, "Failed to read value.", error.toException())
    }
})

Read once using get():

databaseRef.child("users").child(userId).get().addOnSuccessListener {
    Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
    Log.e("firebase", "Error getting data", it)
}

Unnecessary use of get() can increase use of bandwidth and lead to loss of performance, which can be prevented by using a realtime listener as shown above.

For data that only needs to be loaded once and isn’t expected to change frequently, to get the data from the local disk cache instead of checking for an updated value on the server you can use addListenerForSingleValueEvent.

Both setValue() and updateChildren() take an optional completion listener that is called when the write has been successfully committed to the database. If the call was unsuccessful, the listener is passed an error object indicating why the failure occurred.

databaseRef.child("users").child(userId).setValue(user)
        .addOnSuccessListener {
            // Write was successful!
        }
        .addOnFailureListener {
            // Write failed
        }

The simplest way to delete data is to call removeValue() on a reference to the location of that data.
You can also delete by specifying null as the value for another write operation such as setValue() or updateChildren(). You can use this technique with updateChildren() to delete multiple children in a single API call.

When using Firebase Realtime Database in your app along with ProGuard, you need to consider how your model objects will be serialized and deserialized after obfuscation.

If you use DataSnapshot.getValue(Class) or DatabaseReference.setValue(Object) to read and write data, you will need to add rules to the proguard-rules.pro file:

    # Add this global rule
    -keepattributes Signature

    # This rule will properly ProGuard all the model classes in
    # the package com.yourcompany.models.
    # Modify this rule to fit the structure of your app.
    -keepclassmembers class com.yourcompany.models.** {
      *;
    }

for detailed information check out the official Firebase documentation: https://firebase.google.com/docs/database/