Android, Java

Adding A UI Test Case for an Android App

The Need For UI Testing Suites

UI Testing can be boring but it’s still essential for making sure that the app has the proper responses under certain scenarios. That idea inspired me to make an Issue on the TravelMate repository about testing suites and I decided to jump at it by doing a UI Testing Pull Request. This UI testing case actually yielded results because later on I found out that the TravelMate app does not have proper error messages when the email field is invalid (i.e. it does not have the format of “johnsmith@example.com”) and thus the user wouldn’t know why they’re failing to sign up if they didn’t enter their email in the correct format.

Setup For UI Testing

To perform UI testing on the Android platform, we need to use Espresso. Espresso is a testing framework designed for writing UI test cases and it also supports recording certain scenarios in a simulated Android device to make writing test cases easier. The setup for my UI testing is as follows:

build.gradle(module:app)

android {
compileSdkVersion 28
defaultConfig {
applicationId "io.github.project_travel_mate"
minSdkVersion 21
targetSdkVersion 28
versionCode 45
versionName "5.5.2"
testInstrumentationRunner = 'androidx.support.test.runner.AndroidJUnitRunner'
}
dependencies {
...
testImplementation 'junit:junit:4.12'
...

//UI Testing Dependencies
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test:rules:1.1.0'

}

Once that has been added on your build.gradle file for your Android project, you can begin testing by clicking the ‘Run’ button on the top navigation menu and clicking ‘Record Espresso Test’ on the pop-up layout.

RecordEspressoTestOption

The Test Itself

TestingSetupEspresso

The image above demonstrates my testing setup. I basically performed a sequence of actions to create a scenario where a user would submit a Signup form with empty email and password fields. As it turns out, TravelMate app does not have the proper messages to deal with this kind of scenario. The recorded UI testing scenario also generated a code file called EmptyEmailAndPasswordFieldsTest.java. A snippet of code from that file is as follows:

 @Test
    public void emptyEmailAndPasswordFieldsTest() {
...
 ViewInteraction editText = onView(
                allOf(withId(R.id.input_email_signup), withText("Email id"),
                        childAtPosition(
                                childAtPosition(
                                        IsInstanceOf.instanceOf(android.widget.LinearLayout.class),
                                        0),
                                0),
                        isDisplayed()));
        editText.check(doesNotExist());

        ViewInteraction editText2 = onView(
                allOf(withId(R.id.input_pass_signup), withText("Password"),
                        childAtPosition(
                                childAtPosition(
                                        IsInstanceOf.instanceOf(android.widget.LinearLayout.class),
                                        0),
                                0),
                        isDisplayed()));
        editText2.check(doesNotExist());

        ViewInteraction editText3 = onView(
                allOf(withId(R.id.input_confirm_pass_signup), withText("Confirm Password"),
                        childAtPosition(
                                childAtPosition(
                                        IsInstanceOf.instanceOf(android.widget.LinearLayout.class),
                                        0),
                                0),
                        isDisplayed()));
        editText3.check(doesNotExist());

The @Test is an annotation to indicate that the following code block will contain code for testing scenarios. For example, take the following code snippet. This snippet means that we’re testing for a scenario where the “Email id” field is empty:

 ViewInteraction editText = onView(
                allOf(withId(R.id.input_email_signup), withText("Email id"),
                        childAtPosition(
                                childAtPosition(
                                        IsInstanceOf.instanceOf(android.widget.LinearLayout.class),
                                        0),
                                0),
                        isDisplayed()));
        editText.check(doesNotExist());

The testing itself is run successfully by testing benchmarks – it showed us that TravelMate does not have the proper error messages to deal with empty error messages. However, the testing code itself can be further improved by using more assertions in testing where we check if they display a particular error message.

Discovery Of An Issue

As stated earlier, TravelMate does not have the proper error messages to deal with empty email and passwords fields because they assumed that the user would enter them properly. However, beyond that scenario, I also found out that if the user didn’t enter the proper format for their email address, there’s also no error message that is shown as demonstrated in the GIF below:

NoErrorMessages

Open Source, Reflections

Momentary Thoughts About Open Source Work

Open Source Work Is An Adventure

Back in September 2018 when I first started doing open source work as I took the open source class DPS909, I only had the bare idea of what open source work is: software that can be worked on by anyone and anywhere in the world, people have to follow certain guidelines, and open source software is a great alternative to proprietary software (i.e. LibreOffice vs Microsoft Office). I started off small – contributing to a really small project and my post detailing my work can be found here. Then Hacktoberfest came in and I dove in to some open source projects to learn more about them. Since I wanted my career to focus on Android mobile development, I decided to look for open source Android apps on GitHub. I found one successfully and to this day I still contribute to it (albeit on an infrequent basis) – and its name is TravelMate. Although at first I found the project daunting, over time I grew comfortable with it as my knowledge of its codebase expanded and my knowledge of Android development also expanded – particularly in terms of debugging, app widgets, UI layouts, documentation, and Error Messages.

 

Over the course of many projects, I’ve encountered various types of projects – projects which have a huge codebase and multiple maintainers (i.e. WordPress for Android, K9-Mail) and projects with medium size codebase and only one maintainer (i.e. TravelMate and SimpleCalendar). From my comfort level of knowledge and skill, I feel more comfortable approaching medium-size codebases because I feel like I can easily make changes to them and not set off a domino effect on the whole project.

 Small Victories

Since them from September 2018, I’ve had 18 PRs created in different projects. I didn’t really have any number of PRs in mind to achieve – I just simply wanted to learn what I could to improve my coding skills while at the same time gaining other skills that I could use. In particular, I wanted to improve my knowledge of Android development.

PRs

As seen here, only 4 out of my 18 PRs have been successfully merged into projects. Most of these PRs are something on that involved Android development. I would say that out of these changes, the PR that involved adding a Clock Widget to TravelMate has been my favourite one so far. It’s one of my most memorable experiences as I’ve learned something new and actually implemented it successfully in TravelMate. I remember working from 9PM to almost 3AM in the morning just to finish that PR and I felt a huge sense of achievement when I finished it.

Downsides

Of course there’s also some downsides to open source work, particularly on Android open source apps. First off, there’s only a really small amount of open source Android apps that are available in GitHub – from my experience alone when searching for open source Android apps to contribute to, the active projects that I saw are somewhere between 10 – 15. Maybe I’m more biased in this perspective because I only picked open source apps with which I feel more comfortable contributing to but still, something can be said about the low amount of open source Android apps.

Another problem of contributing to open source apps is the problem of communication between the contributor and the maintainer. A great example of this is TravelMate – the maintainer of TravelMate has a full time job and lives in India so when I submit a PR, I need to wait a couple of days before it gets reviewed and when I make changes it can take another couple of days. In other words, the progression can be slow.

Future Open Source Work

Would I continue to contribute to the open source Android apps that I’ve contributed to so far? Yes. Should I tackle larger projects more? Yes. Would I prefer working on open source Android apps instead of working on my personal app projects? Preferably no. Open source work is a great adventure but for now I have my goal in one thing only – to create my own app and publish it on Google Play Store and get some revenue from it. I want that goal in mind so that I can become more experienced in all facets of Android development, no matter how many steps it would take.

Android, Java, Open Source

Sometimes it just works | Less is more

Sometimes It Just Works

On my last open source work update, I was interested at working on K9-Mail and targeted a particular Issue. However, there was something weird with it – at times the Issue was able to be replicated but sometimes it wasn’t able to be replicated. To demonstrate this, let’s take a look at the steps to replicate the problem:

  1. The email author has no PGP key setup in the OpenKeychain app.
  2. Compose mail to PGP recipient.
  3. Toggle Encryption to “Encrypt” (green lock symbol)
  4. Hit the “Send” button.

Email author has no PGP key setup and only the recipient(John Smith) has a PGP key setup (same device):

okcconfiguration

Now, we compose an encrypted email to the recipient and attempt to send it:

K9MailWorking

As it can be seen here, the Toast displayed the appropriate error instead of the “Send Error: Null” that’s reported in the Issue. Sometimes I was able to replicate the same Issue with the exact same steps but I can barely capture it because it rarely happens and I’m sure that there’s another extract step involved in it, and even then I can’t really say for sure that this would require fixing. Until another Issue comes up with this kind of problem, I don’t see how or why should I make a PR for this kind of Issue because the error message is properly displaying most of the time.

Therefore, I wanted to move on to another Issue that can be solved.

A Familiar Place

I checked in on TravelMate’s repo and found an Issue where I knew I could actually finish and can be easily replicated. It’s also a simple matter of refactoring code which I have done before too.

The Problem:

OldPermissions_TravelMate

As seen above, when TravelMate first starts up as an app and the user has not logged in yet, it asks for a bunch of permissions. These permissions are necessary for some functionality in the app but it’s not always necessary, therefore it just bogs down the user’s experience of using the app for the first time if they don’t know what to do with the permissions. That is not the worst case though, the worst case is that a privacy-focused user can just deny all these permissions in the app because they think that these permissions aren’t necessary since they haven’t fully tried out the app yet.

Simple Solutions For Less

Since the code for the runtime permissions is already available, what I basically had to do is just move them to other parts of the app that actually need them. From the LoginActivity.java file and moving it to the HotelsActivity.java:

Old Code

private void getRuntimePermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (ContextCompat.checkSelfPermission(MainActivity.this,
            Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.CAMERA,
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.BLUETOOTH,
                Manifest.permission.BLUETOOTH_ADMIN,
                Manifest.permission.READ_CONTACTS,
                Manifest.permission.WRITE_CONTACTS,
                Manifest.permission.READ_PHONE_STATE,
                Manifest.permission.WAKE_LOCK,
                Manifest.permission.INTERNET,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.VIBRATE,
            }, 0);
        }
    }
}

New Code

//Get permissions for making calls and locations
private void getPermissions() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED)
                {RequestPermissions(new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_CONTACTS,
                    Manifest.permission.WRITE_CONTACTS,
                    Manifest.permission.ACCESS_NETWORK_STATE,
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION,
            }, 0);
        }
    }
}

It’s not much of a coding work, but it’s an honest day’s worth of work that can improve user experience for the app, as can be seen in the GIF below:

Less Is More

 

As shown above, the permissions are now only relegated to being asked when the user selects the ‘Travel’ section of the app because these permissions are actually required to use the features under the Travel section of the app – in this case, possible hotel bookings depending on the user’s location.

Android, Java, Kotlin, Open Source, Reflections

Open Source Work Update, Week of March 22, 2019

Aftermath of Adventure In WordPress

Last week I was doing work on the WordPress For Android repository and over the weekend and this week I have continued to work on the same issue since then to resolve the Issue properly, and I have managed so far to produce 2 solutions: Changing the appropriate string for the specific error message and Using the event.error.message function of the WordPress-FluxC for Android which is responsible for network and persistence capabilities of the WP for Android app by helping to connect and sync data from a WordPress site.

Eyes Up On K9-Mail

Other than that, I have been trying to look at an Issue on K9-Mail for Android repository The Issue pertains to an unclear Toast message that only states “null” and doesn’t state the source of the error.

What is K9-Mail?

K9-Mail is an open source email client for Android. Think of it as a Gmail-like app for your phone or Outlook except that one defining feature of it is that it supports OpenPGP – an encryption standard for encrypting emails to send confidential data or prevent possible snooping of emails from third parties, more of that can be found here.

What’s OpenPGP’s relation to K9-Mail?

OpenPGP is just a standard – that means it can be implemented in any way possible by an API as long as the implementation conforms to the standard. In Android, this implementation was provided by OpenKeychain and K9-Mail uses the OpenKeychain API to encrypt and decrypt messages.

The Issue

The problem can be divided into 2 sections – K9-Mail’s usage of the OpenKeychain API and how error handling is handled in K9Mail. Currently, I’m studying how OpenKeychain is being utilized in K9-Mail:

K9Mail-OpenPGP

As you can see from above, the handleOpenPgpError() function handles the error processing capabilities of the OpenPGP implementation of the OpenKeychain API but this is K9-Mail’s usage of the API and it’s not K9-Mail’s error handling. K9-Mail’s error handling for the specific Issue at hand can be found at the MessageCompose.java file, specifically onMessageBuildException() function:

MsgBuildException

The ‘null’ message in the aforementioned Issue is caused by the ‘me.getLocalizedMessage() function call generating a null return value so obviously something’s wrong with the getLocalizedMessage() method when the OpenKeychain app is not properly setup.

Android, Java, Open Source

An adventure on WordPress for Android

Background

Over the course of the past week, I’ve been working on an Issue in the WordPress for Android repository. The project itself is very overwhelming because of how large it is – building the app itself took a whooping 23 mins just to be compiled. The longest one I’ve ever seen. However, the structure of the application itself is coherent – I was able to easily locate the files that needed to be fixed. To start, the Issue that I tackled can be found here.

A confusing error message

WrongErrorString

As you can see from the above GIF, when I want to change my current linked email address of my WordPress account, to an email that’s also tied to another WordPress account, the app itself only gives a vague error message that the account settings can’t be saved – it doesn’t state the fact that the email address itself is an issue and a less technical person might not be able to correctly diagnose the cause of the error.

Debugging in WordPress for Android

Now, before I’m able to produce the error message in the GIF above, I needed to create an app in the WordPress Developer App Manager. Why? Because apparently we need our own OAuth information so that the WordPress for Android app that’s installed on my emulator phone can enable to log-in to my WordPress account and test experimental features.

As seen here below, I created the app necessary to generate OAuth information:WPAndroidTesting

OAuth Information

For privacy purposes, I cleared out my own OAuth information details. When you create your own app, you’ll see your own OAuth information below and you can use the “Client ID” and “Client Secret” information fields for the app by inputting them into the gradle.properties file into the WP.OAUTH.APP_ID and WP.OAUTH.APP_SECRET fields.

WPAndroidOauthSettings

Now that proper setup for the debugging is complete, I was now able to make changes and test them. The needed fix is a simple change to the Toast message for the app by creating a string named error_email_used_settings in the strings.xml file and attribute the message “That e-mail address is already being used.” to it. Now that it’s in the strings.xml file, I was able to use it as an error message for the Toast. As shown below, the ToastUtils.showtoast(getActivity(), R.string.email_used_settings, Toast.Utils.Duration.LONG) code snippet is the one responsible for generating the error message as a Toast.

CodeSnippet

A helpful error message

And now we have a more coherent error message that tells the user what exactly went wrong when they weren’t able to save their new email address.

CorrectErrorString