Upgrading the Style and Aesthetic of an Android App using Material Design

I often encounter apps as I add Open Event format support that don’t follow current design guidelines. Earlier styling an app was a tough task as the color and behaviour of the views needed to be defined separately. But now as we move forward to advanced styling methods we can easily style our app.

I recently worked on upgrading the user interface of Giraffe app after adding our Open Event support. See the repository to view the code for more reference. Here I follow the same procedure to upgrade the user interface.

First we add essential libraries to move with our material aesthetic. The Appcompat library provides backward compatibility.

//Essential Google libraries
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:design:25.3.1

Then we define an XML file in the values folder for the style of the app which we get through Appcompat library. We could inherit same style in the entire app or separate style for the particular activity.

<resources>

   <!-- Base application theme. -->
   <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
       <!-- Customize your theme here. -->
       <item name="colorPrimary">@color/colorPrimary</item>
       <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
       <item name="colorAccent">@color/colorAccent</item>
   </style>


   <style name="AlertDialogCustom" parent="Theme.AppCompat.Light.Dialog.Alert">
       <item name="colorPrimary">@color/colorPrimary</item>
       <item name="colorAccent">@color/colorAccent</item>
   </style>

</resources>

So now we can see the views made following the same color scheme and behaviour throughout the app following current design guidelines without any particular manipulation to each of them.

Tip: Don’t define values of colors separately for different views. Define them in colors.xml to use them everywhere. It becomes easier then to change in future if needed.

The app now uses Action Bar for the frequently used operations unlike the custom layout that was made earlier.

This is how Action Bar is implemented,

First declare the action bar in XML layout,

Tip: Define color of the bar two shades lighter than the status bar.

 <android.support.design.widget.AppBarLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:background="@android:color/transparent"
             android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
             <android.support.v7.widget.Toolbar

                 xmlns:app="http://schemas.android.com/apk/res-auto"         
                 android:id="@+id/toolbar_options"
                 android:layout_width="match_parent"
                 android:layout_height="?attr/actionBarSize"
                 android:background="@color/colorPrimary"
                 app:popupTheme="@style/ThemeOverlay.AppCompat.Dark">
                 
                 <TextView
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:text="@string/options"
                     android:textColor="@color/colorAccent"
                     android:textSize="20sp" />
              </android.support.v7.widget.Toolbar>

</android.support.design.widget.AppBarLayout>

Then you can use the action bar in the activity, use onCreateOptionsMenu() method to inflate options in the toolbar.

@Override
    public void onCreate(Bundle savedInstanceState) {
        ...

        setTitle("");
        title = (TextView) findViewById(R.id.titlebar);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_main);
        setSupportActionBar(toolbar);

        ...
    }

The menu that needs to be inflated will be like this for two button at the right end of the action bar for bookmarks and filter respectively,

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
     <item
        android:id = "@+id/action_bookmark"
        android:icon = "@drawable/ic_bookmark"
        android:menuCategory = "secondary"
        android:title = "Bookmark"
        app:showAsAction = "ifRoom" />
 
     <item
         android:id = "@+id/action_filter"
         android:icon = "@drawable/ic_filter"
         android:menuCategory = "secondary"
         android:title = "Filter"
         app:showAsAction = "ifRoom" />
</menu>

To adapt the declared style further, Alert Dialogs are also modified to match the app’s theme, it’s style is defined along with the app’s style. See below

AlertDialog.Builder noFeedBuilder = new AlertDialog.Builder(context,R.style.AlertDialogCustom);
            noFeedBuilder.setMessage(R.string.main_no_feed_text)
                    .setTitle(R.string.main_no_feed_title)
                    .setPositiveButton(R.string.common_yes, new DialogInterface.OnClickListener() {
                  ...
            noFeedBuilder.show();

Here is an example of improvement, before and after we update the user interface and aesthetic of app in easy steps defined,

   

See this for all the changes made to step up the user interface of the app.

References:

 

Continue Reading

Importing the Open Event format in Giraffe

Giraffe is a personal conference schedule tool for Android. Most conferences, bar camps and similar events offer their plan of sessions and talks in the iCal format for importing into your calendar. However importing a whole session plan into your standard calendar renders it pretty much useless for anything else. Giraffe allows users to import the schedule into a separate list giving you a simple overview on what happens on the conference. Besides the session, title, date and  time it also lists the speaker, location and description if available in the iCal URL. Sessions can be bookmarked and the list can be filtered by favourites and upcoming talks.

Recently I added the support for Open Event JSON format along with iCal. In this blog I describe the simple steps you need to follow to see the event that is created in the Open Event server in the Giraffe app. The initial steps are similar to Giggity app,

 1. Go to your event dashboard

2. Click on the export button.

3. Select sessions from the dashboard and copy the URL.

 

4. Click on the “Giraffe” button on the toolbar and paste the link in the box following. App will ask you to paste it when the first time you open it. Here the app loads the data and checks few initial character to see which kind of data is received. Find my other blog post to solve that problem here.

The app uses separate data models for iCal and JSON to store the informations received and then save them in SQL database for CRUD options. See the database activity here

   

5. Now you can see the sessions. Click on them to see more information or bookmark them if needed. The data is loaded from the database so when app is offline so we don’t need to worry about connection once the data is being loaded.

   

Resources:

 

Continue Reading

Viewing an Event in Giggity

Giggity is an Android app that loads xCal, pentabarf, frab, wafer and iCal files (that contain schedules of conferences, festivals and other events) and lets you browse them in various convenient formats. It is a generic app that can be used for any event that publishes their schedule in an open format. Recently support to see Open Event JSON format has been added in the Giggity. In this blog I describe the simple steps you need to follow to see the event that is created in the Open Event server and can be seen in the Giggity app. Although the process of creating an event is quite detailed but the UI is very user friendly and simple to use so here I describe the process after creating the event and adding it in Giggity app

1. Go to your event dashboard

2. Click on the export button

3. Select sessions and microlocations by clicking the switch buttons. Get the url generated and copy it. Skipping the microlocations will disable the option to navigate to the location in map opening from the app.

4. Click on the “+” button on the toolbar and paste the link.

   

5. Now you can see the sessions with locations. Slide the navigation drawer to see the links and signup url or create the home screen shortcut.

   

So here you can see your event following these five simple steps. Watch this video to see this in action.

https://www.youtube.com/watch?v=RairLTXqqDI

Continue Reading

Differentiating between received file formats for Event Apps

Sometimes we need to operate on files of different formats in the same app itself for example Giggity app parses the file in iCal, xCal, pentabarf and now in Open Event JSON format.

It could be problematic as every format needs different type of manipulation to read and get the relevant data from it, for example the JSON format is based on objects and arrays in it from which we can get the data by referencing titles of the data while pentabarf XML identifies the elements with tags. Also it is not evident from the link itself which format is being received. So to see which type of file is received instead of analyzing the entire file we look for the few initials of the syntax from which it becomes evident.

I recently used this method to differentiate between the JSON and iCal format to display sessions of an event in the Giraffe app in Open Event JSON format which is also being used in Giggity.

Let’s have a look on how to actually implement this. It is simple after you get the data from the url. Instead of iterating for the entire data we read the initial data only to see what kind of file we have received.

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String line = r.readLine();

if(line.contains("BEGIN:VCALENDAR")) {
 SharedPreferences.Editor edit = prefs.edit();
 edit.putString("type", "ical");
 edit.commit();
 Log.e("Response type","ical");
} 
else if(line.contains("{")) {
 SharedPreferences.Editor edit = prefs.edit();
 edit.putString("type", "json");
 edit.commit();
 Log.e("Response type","json");
}

We saved the type of response received in the shared preferences so we retrieve it later from anywhere in the app to see what kind of data we have received instead of passing it in between functions or activities.

See this to check as you download the data from the URL asynchronously, so you don’t need to do it later or manipulations needs to be done asynchronously too.

So when you are checking in asynchronously using AsyncTask class get shared preferences in onPreExecute() and update it in doInBackground() as we download the data and check.

The other advantage of this method is that we can close the thread or return to the main activity if the file format found is not readable or not of desired format.

Continue Reading

Using AutoCompleteTextView for interactive search in Open Event Android App

Providing a search option is essential in the Open Event Android app to make it easy for the user to see the desired results only. But sometimes it becomes difficult to implement this with a good performance if the data set is large, so providing simply a list to scroll through may not be enough and efficient. AutoCompleteTextView provides a way to search data by offering the suggestions after a user types in some initial letters of the search query.

How does it work? Actually we feed the data to an adapter which is attached to the view. So, when a user starts typing the query the suggestions starts appearing with similar names in the form of the list.

For example see above. Typing “Hall” gives the user suggestion to pick up the entry which have word “Hall” in it. Making it easier for user to search.

Let’s see how to implement it. For the first step declare the view in XML layout like this. Where our view goes by the id “map_toolbar” and white text colour for the text that will be appearing in it. Input type signifies that the autocomplete and auto correct is enabled.

<AutoCompleteTextView
       android:id="@+id/map_toolbar"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:ems="12"
       android:hint="@string/search"
       android:shadowColor="@color/white"
       android:imeOptions="actionSearch"
       android:inputType="textAutoComplete|textAutoCorrect"
       android:textColorHint="@color/white"
       android:textColor="@color/white"
/>

Now initialise the adapter in the fragment/activity with the list “searchItems” containing the information about the location. This function is in a fragment so modifying things accordingly. “textView” is the AutoCompleteTextView that we initialised. To explain this function further when a user clicks on any item from the suggestions the soft keyboard hides. You can do define desired operation here. 

Setting up AutoCompleteTextView with the locations

ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_dropdown_item_1line, searchItems);
textView.setAdapter(adapter);

textView.setOnItemClickListener((parent, view, position, id) -> {

Things you want to do on clicking the item

View mapView = getActivity().getCurrentFocus();

if (mapView != null) {
  InputMethodManager imm =     (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
  imm.hideSoftInputFromWindow(mapView.getWindowToken(), 0);
}
});

See the complete code here to find the implementation of AutoCompleteTextView in the map fragment of Open Event Android app.

Continue Reading

Managing Edge Glow Color in Nested ScrollView in Open Event Android App

After the introduction of material design by Google many new UI elements have been introduced. Material design is based on the interaction and movement of colours and objects in real world rather than synthetic unnatural phenomenon. Sometimes it gets messy when we try to keep up with our material aesthetic. The most popular example of it is edge glow colour. The edge glow colour of ListView, RecyclerView, ScrollView and NestedScrollView is managed by the accent colour declared in the styles. We cannot change the accent colour of a particular activity as it is a constant, so it is same across entire app but in popular apps like Contacts by Google it appears different for every individual contacts. Fixing an issue in Open Event Android app, I came across the same problem. In this tutorial I solve this problem particularly for NestedScrollView. See the bottom of screenshots for comparison.

   
  • You need to pre check if the Android version is above or equal to LOLLIPOP before doing this as the setColor() function for edge glow is introduced in LOLLIPOP
  • The fields are declared as “mEdgeGlowTop” and “mEdgeGlowBottom” that we have to modify.
  • We get the glow property of NestedScrollView through EdgeEffectCompat class instead of EdgeEffect class directly, unlike for ListView due its new introduction.

Let’s have a look at the function which accepts arguments color as an integer and nested scroll view of which the color has to be set.

First you have to get the fields “mEdgeGlowTop” and “mEdgeGlowBottom” that signifies the bubbles that are generated when you scroll up and down from Nested Scroll View Class. Similarly “mEdgeGlowLeft”  and “mEdgeGlowRight” for the horizontal scrolling.

public static void changeGlowColor(int color, NestedScrollView scrollView) {
   try {

       Field edgeGlowTop = NestedScrollView.class.getDeclaredField("mEdgeGlowTop");

       edgeGlowTop.setAccessible(true);

       Field edgeGlowBottom = NestedScrollView.class.getDeclaredField("mEdgeGlowBottom");

       edgeGlowBottom.setAccessible(true);

Get the reference to edge effect which is the different part unlike recycler view or list view of setting the edge glow color in nested scrollview.

EdgeEffectCompat edgeEffect = (EdgeEffectCompat) edgeGlowTop.get(scrollView);

       if (edgeEffect == null) {
           edgeEffect = new EdgeEffectCompat(scrollView.getContext());
           edgeGlowTop.set(scrollView, edgeEffect);
       }

       Views.setEdgeGlowColor(edgeEffect, color);

       edgeEffect = (EdgeEffectCompat) edgeGlowBottom.get(scrollView);
       if (edgeEffect == null) {
           edgeEffect = new EdgeEffectCompat(scrollView.getContext());
           edgeGlowBottom.set(scrollView, edgeEffect);
       }

       Views.setEdgeGlowColor(edgeEffect, color);

   } catch (Exception ex) {

       ex.printStackTrace();
   }

}

Finally set the edge glow color. This only works for the versions that are above or equal to LOLLIPOP as edge effect was introduced in the android beginning from those versions.

@TargetApi(Build.VERSION_CODES.LOLLIPOP)

public static void setEdgeGlowColor(@NonNull EdgeEffectCompat edgeEffect, @ColorInt int color) throws Exception {
        Field field = EdgeEffectCompat.class.getDeclaredField("mEdgeEffect");

        field.setAccessible(true);
        EdgeEffect effect = (EdgeEffect) field.get(edgeEffect);
        
        if (effect != null)
            effect.setColor(color);
    }

 

(Don’t forget to catch any exception. You can monitor them by using Log.e(“Error”, ”Message”, e ); for debugging and testing).

Resources

  • https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html
Continue Reading

Testing User Interactions with Espresso

Espresso is a testing framework which provides the facility to write the tests for user interactions and unitary tests. Since the release of its version 2 it is now a part of Android Testing Support Library.

The android apps we build at FOSSASIA follow rigorous testing methods. See this simple UI test  in the Phimp.me app using espresso to check if button and bottom navigation are displayed in an activity. You can also find our other tests related to API and databases in the Open Event Android App.

In this blog we learn how to add this facility to your app and write a test for a simple app that takes the name of from the user and prints it on the other screen on button click.

Adding espresso support

  • Install android support repository if not already present. You do it by following Tools -> Android -> SDK Manager
Tools you need to download for testing
  • Add the following dependencies to your app’s build.gradle file
dependencies {
    androidTestCompile 'com.android.support.test:runner:0.5'
    androidTestCompile 'com.android.support.test:rules:0.5'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'

}
  • Specify the test instrumentation runner in default config
android {

    defaultConfig {

        // ....

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

}

Before we begin with writing our tests knowing some basic components will help in understanding the code better. Writing tests with espresso is easy as its construction is similar to English language.

The three major components are 

  • ViewActions        : Allows you to interact with views
  • ViewAssertions   : Allows you to assert the state of a view.
  • ViewMatchers     : Allows you to locate a view in the current view hierarchy.

Suppose we want to test if text is displayed in the view, we can do it by

onView(withId(R.id.textView))                              //ViewMatcher

 .perform(click())                                         //ViewAction

 .check(matches(isDisplayed()));                           //ViewAssertion

Example

Consider an app which takes a name from the user and displays it on the next screen on clicking the button.

To perform this kind of test we will write

//Locate the view with id "name" and type the text "Natalie"

onView(withId(R.id.name)).perform(typeText("Natalie"));

//Locate the view with id "next" and click on it

onView(withId(R.id.next)).perform(click());

//Locate the view with id "new_name" and check its text is equal with "Natalie"

onView(withId(R.id.new_name)).check(matches(withText("Natalie")));

You can run tests by right clicking on the class and selecting the “run test” option. If the interaction is not as expected then the message will be displayed.

Up until now unit test were in main focus but as we move towards the more complex apps where user interaction plays an essential role, UI testing becomes equally necessary.

References:

Continue Reading
  • 1
  • 2
Close Menu