Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Android User Interactivity and Sensors

, 24 Aug 2014
Rate this:
Please Sign up or sign in to vote.
Getting to know and implement the various android interactivity and sensors with the aim to enhance the users' experience with your apps.

Introduction

We have discussed Android UI extensively in my article Android UI Layouts and Controls. UI presents to the users the first impression of your app based on the look of the layout and UI controls. However, this is only one side of the formula. When the users start to interact with your app, they will derive their own feeling about your app from the interaction, that feeling will determine whether the users are going to stay or dump your app. That is the power and subtleness of User Experience, or UX in short. This article will introduce the various interactive features and sensors in Android that can help to enhance the UX of your app. Specifically, we will focus on these topics:

You will get plenty of hands-on practices as we walk the talk.

Getting Ready

First of all, you will get ready a new Android project as follows:

  • Name the new project as "AndroidUX".

  • In the new project, create an Activity called "MainActivity", this will be the home page that provides the buttons to navigate to other pages.

  • The "activity_main.xml" is shown as such,

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context=".MainActivity">
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/gestures"
            android:id="@+id/button"
            android:layout_alignParentTop="true"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true" />
    </RelativeLayout>

    The "MainActivity" should look like this,

    Figure 1: MainActivity

We will start with Gestures.

Gestures

Almost all mobile devices come with touch screen. To interact with any app on a touch screen, the most natural and common input device is your fingers. A single finger can produce many types of interactions, from the all-too-familiar touch, double-touch, scroll, and drag to the newer swipe and pinch. They are all known as Gestures (Table 1).

Table 1: Type of Gestures
Gesture Sequence of Actions Functionality

Touch

Press > Lift

This triggers the default functionality for the touched View. It is the starting point of all other gestures.

Double Touch Two touches in quick succession Scale up or down a View.

Long Press

Press > wait > lift

It is commonly used to activate a contextual action bar for item selection.

Swipe Press > move > lift
in quick succession
To scroll content that spill over a viewport. The screen will continue to react for a brief moment even after the finger is lifted.
Drag Press > move > lift
in slow succession

To scroll content that spill over a viewport. The screen stops responding once the finger is lifted. It is slower and thus more precise than the Swipe gesture.

Long Press Drag Long Press > move > lift

To rearrange item without a View, or move it to different home screen.

Pinch Open Two-finger press > move the two fingers apart > lift

To zoom in to a View.

Pinch Close Two-finger press > move the two fingers closer > lift

To zoom out of a View.

The root of all gestures is the "touch gesture". A touch gesture occurs when a user places one or more fingers on the touch screen. All other gestures occur as a sequence of touch events after the initial "touch down". Each gesture produces data that can be captured and interpreted in your code to determine the type of gesture and the corresponding action to take.

Let's put this in code perspective. The View class in Android comes with an onTouchEvent() callback method that you can override to receive the touch event whenever the screen is being touched. When the user touches a View, it triggers the "onTouchEvent()" method on that View. The subsequence actions that followed such as position, duration, position, size, addition of another finger, etc, will trigger the "onTouchEvent()" successively. Each action will pass a MotionEvent object to "onTouchEvent()" providing the details of the interaction. Your app can then use the data provided by the "MotionEvent" to determine if a gesture occurs and what corresponding action to take.

Getting in Touch

In the Android project,

  • Create a new Activity called "GesturesActivity".

  • In the "dimens.xml", add this dimension resource,

    <dimen name="robot_size">200dp</dimen>
  • The "activity_gestures.xml" is shown as such,

    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        xmlns:android="http://schemas.android.com/apk/res/android">
        <ImageView
            android:layout_width="@dimen/robot_size"
            android:layout_height="@dimen/robot_size"
            android:id="@+id/androidRobot"
            android:src="@drawable/ic_launcher"
            android:scaleType="matrix"
            android:layout_gravity="center" />
    </FrameLayout>

    The preview should look like this (Figure 2).

    Figure 2: Preview of GesturesActivity
  • Our first requirement is to capture any gesture occurred on the page. To achieve this, we will engage the help of the "GestureDetector" class.

    Quote:

    GestureDetector detects various gestures and events using the supplied MotionEvents. The GestureDetector.OnGestureListener callback will notify users when a particular motion event has occurred. This class should only be used with MotionEvents reported via touch (don't use for trackball events). To use this class:

  • Follow these steps to add the required code to the "GesturesActivity.java"

    • Import "GestureDetector" class to the "GesturesActivity.java",

      import android.view.GestureDetector;
    • Implement the two interfaces of "GestureDetector" class, i.e. "OnGestureListener" and "OnDoubleTapListener" to listen for a gesture event and notify your app when it occurs, declare a "GestureDetector" object, and declare a String called "DEBUG_TAG".

      public class GesturesActivity extends Activity implements
              GestureDetector.OnGestureListener,
              GestureDetector.OnDoubleTapListener{
      
          private static final String DEBUG_TAG = "Gesture Detected";
      
          private GestureDetector gestureDetector; // declare a "GestureDetector" object
      
          // other code
    • Instantiate a "GestureDetector" object called "gestureDetector" and set an "OnDoubleTapListener" on it.

      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_gestures);
      
          gestureDetector = new GestureDetector(this, this);
      
          gestureDetector.setOnDoubleTapListener(this);
    • You will have to implement all the abstract methods of the two interfaces that you have implemented like this,

          @Override
          public boolean onDown(MotionEvent event) {
              Log.d(DEBUG_TAG, "onDown: " + event.toString());
              return true;
          }
      
          @Override
          public boolean onFling(MotionEvent event1, MotionEvent event2,
                                 float velocityX, float velocityY) {
              Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());
              return true;
          }
      
          @Override
          public void onLongPress(MotionEvent event) {
              Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
          }
      
          @Override
          public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                                  float distanceY) {
              Log.d(DEBUG_TAG, "onScroll e1: " + e1.toString() + "; e2: " + e2.toString());
              return true;
          }
      
          @Override
          public void onShowPress(MotionEvent event) {
              Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
          }
      
          @Override
          public boolean onSingleTapUp(MotionEvent event) {
              Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
              return true;
          }
      
          @Override
          public boolean onDoubleTap(MotionEvent event) {
              Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
              return true;
          }
      
          @Override
          public boolean onDoubleTapEvent(MotionEvent event) {
              Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
              return true;
          }
      
          @Override
          public boolean onSingleTapConfirmed(MotionEvent event) {
              Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
              return true;
          }
    • Lastly, register the "onTouchEvent()" of the "gestureDetector" object to receive the "MotionEvent" data from the "onTouchEvent()" of the View, in this case, the "GesturesActivity".

          @Override
          public boolean onTouchEvent(MotionEvent event){
      
              // register the gestureDetector to the onTouchEvent
              gestureDetector.onTouchEvent(event);
      
              return super.onTouchEvent(event);
          }
  • You have just created a "GestureDetector" object to implement all the methods of its two listener interfaces, and to attach this object to "onTouchEvent()" method of the "GesturesActivity". From now on, any gesture occurs on the ""GesturesActivity" will be picked up by the various methods. Currently, these methods will simply echo the gesture type and the corresponding "MotionEvent" data to the "logcat" console.

  • Before you can test it on an AVD, you will have to create navigation from the "MainActivity". Do these:

    • In the "activity_main.xml", add the "android:onClick" attribute to the <Button> node as shown,

      android:onClick="getGesturesActivity"
    • In the "MainActivity.java", add the "getGesturesActivity()" method to navigate to the "GesturesActivity" when the button is clicked.

      public void getGesturesActivity(View view) {
          Intent intent = new Intent(getApplicationContext(), GesturesActivity.class);
          startActivity(intent);
      }
  • You can now test run the app on an AVD or a physical device. Try out the various gestures on the device and watch the "MotionEvent" data streaming on the "logcat" console (Figure 3).

    Figure 3: logcat
  • If you look closely at the console, you would have noticed that all gestures will start off with the "onDown" event, and every gesture will follow by a sequence of events. For example, even a simple "touch" gesture will involve "onDown", "onSingleTapUp", and "onSingleTapConfirmed" in a succession manner.

Swiping to Navigate

Shall we add some favor to the project, how about a right swipe gesture to move back to "MainActivity" and a left swipe gesture to another Activity (yet to be created) from "GesturesActivity".

In the "onFling()" method of the "GesturesActivity", add the code accordingly as shown.

@Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
                           float velocityX, float velocityY) {
    Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());

    if (event2.getX() > event1.getX()) { // swipe to the right
        Intent intent = new Intent(getApplicationContext(), MainActivity.class);
        startActivity(intent);
    } else if (event2.getX() < event1.getX()) { // swipe to the left
        Intent intent = new Intent(getApplicationContext(), MainActivity.class);
        startActivity(intent);
    }

    return true;
}

The swipe gesture will pass the initial down "MotionEvent" and the matching up "MotionEvent". The code compares the initial x coordinate of the finger against the final x coordinate of the finger to determine whether it is a right swipe or left swipe. Got it?

Try it out on an AVD and it should work like a charm. At the moment, it will always navigate to "MainActivity" regardless of right or left swipe. You shall modify it to other Activity subsequently as you add more Activities.

Pinching to Zoom

We will add the code to perform zooming on the Android robot on "GesturesAcvitity" through pinching.

  • To achieve this, we will engage the help of the "ScaleGestureDetector" class.
    Quote:

    ScaleGestureDetector detects scaling transformation gestures using the supplied MotionEvents. The ScaleGestureDetector.OnScaleGestureListener callback will notify users when a particular gesture event has occurred. This class should only be used with MotionEvents reported via touch. To use this class:

  • Follow these steps to add the required code to the "GesturesActivity.java"

    • Import the following packages to the "GesturesActivity.java",

      import android.view.ScaleGestureDetector;
      import android.widget.ImageView;
      import android.graphics.Matrix;
      import android.content.Intent;
    • Declare these member variables,
      private ScaleGestureDetector scaleGestureDetector;
      private ImageView imageView;
      private Matrix matrix = new Matrix();
      private float scale = 1f;e
    • Add this code to the "onCreate()" method. It will set an "OnTouchListener" to the ImageView that contains the android robot. Whenever a touch gesture occurs on the ImageView it will trigger the scaleGestureDetector's onTouch event.

      scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());
      
      imageView = (ImageView)findViewById(R.id.androidRobot);
      
      imageView.setOnTouchListener(new View.OnTouchListener() {
          public boolean onTouch(View v, MotionEvent event) {
              scaleGestureDetector.onTouchEvent(event);
              return true;
          }
      });
    • Create an inner class called "ScaleListener" that extends "ScaleGestureDetecto.SimpleOnScaleGestureListener" to override the "onScale()" method that does the scaling job.

      private class ScaleListener extends ScaleGestureDetector.
                  SimpleOnScaleGestureListener {
          @Override
          public boolean onScale(ScaleGestureDetector gestureDetector) {
      
              scale *= gestureDetector.getScaleFactor();
      
              scale = Math.max(0.1f, Math.min(scale, 4.0f));
      
              matrix.setScale(scale, scale);
      
              imageView.setImageMatrix(matrix);
      
              return true;
          }
      }  
  • Run it on a real device, you should be able to zoom in and out of the image by pinching.

Keyboard

The Android system supports keyboard input for text field either through an on-screen soft keyboard or a hardware keyboard. It is important that your app is optimized to take care of both scenarios so as to enhance user experience (UX).

Every text field expects a certain type of text input depending on its purpose, such as email, phone number, or plain text. It will be a great UX if your app can auto display the type of soft keyboard that best matched the intended purpose of that text field whenever that text field is on focused. In addition, you app should provide input assistance to the users through such functionalities like spelling suggestions and auto capitalization of the first letter of every new sentence. Yes, the "EditText" widget of Android has these capabilities but you have to know how to configure it to make them work.

Specifying Input Type

In the current project,

  • Create a new Activity called "KeyboardActivity" that incorporates "RelativeLayout" layout and one "EditText" control.

  • The "activity_keyboard.xml" is shown as such,

     <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="com.peterleow.androidux.KeyboardActivity">
        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/editText"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="62dp"
            android:hint="Text Field" />
    </RelativeLayout>  
  • Before you can test it on an AVD, you will have to create navigation from the "MainActivity". Do these:

    • In the "activity_main.xml", add a new "Button" control below the first button, and then add the "android:onClick" attribute to the new <Button> node as shown:

      <Button
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/keyboard"
          android:id="@+id/button2"
          android:onClick="getKeyboardActivity"
          android:layout_below="@+id/button"
          android:layout_alignParentLeft="true"
          android:layout_alignParentStart="true"
          android:layout_marginTop="44dp"
          android:layout_alignRight="@+id/button"
          android:layout_alignEnd="@+id/button" />

      The preview of "MainActivity" should now look like this (Figure 4),

      Figure 4: MainActivity in Preview
    • In the "MainActivity.java", add the "getKeyboardActivity()" method to navigate to the "KeyboardActivity" when the button is clicked.

      public void getKeyboardActivity(View view) {
          Intent intent = new Intent(getApplicationContext(), KeyboardActivity.class);
          startActivity(intent);
      }
  • Run the app on an AVD, click the "Keyboard" button to navigate the "KeyboardActivity" page, and then click inside the "EditText" text field to activate the soft keyboard as shown in Figure 5. This is the keyboard for plain text.

    Figure 5: Soft Keyboard for Plain Text
  • In the "activity_keyboard.xml", add the "android:inputType" attribute to the <EditText> node as shown,

    android:inputType="textEmailAddress" 
  • Re-launch your app, navigate to the "KeyboardActivity" page, and then click on the text field again. You should see a slightly different soft keyboard with a "@" key as shown in Figure 6.

    Figure 6: Soft Keyboard for Email Input Type
  • In the "activity_keyboard.xml", change the "android:inputType" attribute to the <EditText> node as shown,

    android:inputType="number" 
  • Re-launch your app, navigate to the "KeyboardActivity" page, and then click on the text field again. You should see a soft number pad as shown in Figure 7.

    Figure 7: Soft Keyboard for Number Input Type

You have just discovered that the "android:inputType" attribute of the "EditText" control can be used to indicate the type of text to be entered to the Android system which in turn provides the best matching soft keyboard for use. Some of the more common input type values for this type of purpose are:

  • "text" which is the default and calls for a normal text keyboard.

  • "textEmailAddress" which calls for normal text keyboard that includes the @ key.

  • "number" which calls for a basic number keypad.

  • "phone" which calls for a phone dial pad.

In addition, the "android:inputType" attribute can also define other keyboard behaviors, such as to suggest a list of words while you type, mask password text, allow multi-line input, or to capitalize a new sentence, and many more. Some of these input types are:

  • "textPassword" which calls for normal text keyboard, but mask the text entered.

  • "textMultiLine" which calls for normal text keyboard that allows users to enter long strings of text that include line breaks.

  • "textCapSentence" which calls for normal text keyboard that capitalizes the first alphabet of each new sentence.

  • "textAutoCorrect" which calls for normal text keyboard that provides helping words to correct common spelling errors.

You can specify multiple input types to the "android:inputType" attribute of an "EditText" using the "|" separator. Change the "android:inputType" attribute of the "EditText" in the "activity_keyboard.xml" as shown and re-launch it on the AVD:

android:inputType="text|textAutoCorrect"

You will be presented with a plain text soft keyboard. When you make some typo while typing, a list of suggested words will appear to help you (Figure 8).

Figure 8: Soft Keyboard with Auto Correct Function

Customizing Input Action Button

You may have noticed that the soft keyboard has provided an action button on the bottom right corner. You can choose to customize it to suit your need.

  • In the "activity_keyboard.xml", add a new attribute called "android:imeOptions" with a value of "actionSend" to the <EditText> node as shown,

    android:imeOptions="actionSend"
  • Run it on the AVD, the action button has been changed to the Send icon as shown in Figure 9. Refer to android:imeOptions for more options to customize the action button of the soft keyboard.

Figure 9: Send Action Button

Any Other Matters

Normally, the soft keyboard will only appear when the text field receives focus, i.e. when you click on it. What if you want the soft keyboard to appear automatically when the Activity is loaded? For that, you have to add an attribute called "android:windowSoftInputMode" to the "AndroidManifest.xml" of that Activity.

  • In the "AndroidManifest.xml", add a new attribute called "android:windowSoftInputMode" with a value of "stateVisible" to the "KeyboardActivity" Activity node as shown,

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.peterleow.androidux" >
    
        <application ...>
    
            <activity
                <!-- other activitiy -->
            </activity>
    
            <activity
                android:name=".KeyboardActivity"
                android:label="@string/title_activity_keyboard"
                android:windowSoftInputMode="stateVisible" >
            </activity>
    
        </application>
        <!-- other declarations -->
    </manifest>
  • When you re-launch the app and navigate to the "KeyboardActivity" page, the soft keyboard will appear automatically.

  • Let's add one "Button" control near the bottom of the screen in the "activity_keyboard.xml" as shown in Figure 10.

    Figure 10: Add New Button
  • If you re-launch the app and navigate to "KeyboardActivity", you will see the same screen as shown in Figure 9. The newly added button has been hidden behind the soft keyboard. You may wish to make the button moves above the soft keyboard by adding "adjustResize" to the "android:windowSoftInputMode" like this,

    android:windowSoftInputMode="stateVisible|adjustResize"
  • You wish has come true (Figure 11). Refer to android:windowSoftInputMode for more options.

    Figure 11: Add New Button

Re-visiting Swipe Gesture

Remember the swipe gesture that you have attempted previously. Now that you have three Activities, you can re-visit the "GesturesActivity.java" to change the code for "swiping to the left" which will navigate to the "KeyboardActivity" like this,

@Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
                           float velocityX, float velocityY) {
    // other code

    } else if (event2.getX() < event1.getX()) { // swipe to the left
        Intent intent = new Intent(getApplicationContext(), KeyboardActivity.class);
        startActivity(intent);
    }
    return true;
}

GPS

Every one of us occupies a location on Earth. This location is specified by a geographic coordinate system of latitude, longitude, and altitude. With the proliferation of location-aware hardware and software, finding one's location on the Earth has never been easier. There are many techniques available to identify the location of a user. A computer browser generally uses WIFI or IP based positioning techniques whereas a mobile browser may use cell triangulation that based on your relative proximity to the different cellular towers operated by the telco's, GPS, A-GPS, or WIFI. Today, location awareness is an ever-growing trend that finds its way into many applications like:

  • Showing one's location on a map especially when you are in an unfamiliar area.

  • Providing turn-by-turn navigation while driving on unfamiliar journey.

  • Find out the points of interest in one's vicinity.

  • Getting the latest weather or news of one's area.

  • Tagging the location of picture.

  • Location tracking of a fleet of delivery trucks.

The Android system provides the "LocationManager" class for accessing the system location service. You can request location updates from the "LocationManager" by calling its "requestLocationUpdates()" method and passing it a "LocationListener". The "LocationManager" will call the various callback methods of the "LocationListener" whenever the location of the Android device changes or when the status of the location service changes. Let's step through the steps of implementing a simple location service using the GPS in our project.

In the current project,

  • Create a new Java class called "GPSHelper.java" to handle all the GPS related chores (Figure 12)

    Figure 12: Add New Java Class
  • Add the following code to the "GPSHelper.java". This code will get the device current location from the GPS through the "LocationManager". In this exercise, we are requesting location updates from the GPS provider as indicated by this parameter "LocationManager.GPS_PROVIDER". If you want to request it from the Network Location Provider, substitute NETWORK_PROVIDER for GPS_PROVIDER for NETWORK_PROVIDER. You can also request location updates from both the GPS and the Network Location Provider by calling "requestLocationUpdates()" separately. The essential lines of code have been commented for clarity and explanation.

    package com.peterleow.androidux;
    
    /**
     * Created by Peter Leow on 23/8/2014.
     */
    import android.location.LocationListener;
    import android.location.LocationManager;
    import android.app.Service;
    import android.content.Context;
    import android.content.Intent;
    import android.location.Location;
    import android.os.Bundle;
    import android.os.IBinder;
    
    public class GPSHelper extends Service implements LocationListener {
        private final Context context;
        
        boolean isGPSEnabled = false;
        Location location;
        double latitude;
        double longitude;
        // minimum distance to update location in meters
        private static final long MIN_DISTANCE_CHANGE_TO_UPDATES = 5; // 5m
        // minimum time between updates in milliseconds
        private static final long MIN_TIME_ELAPSED_BETWEEN_UPDATES = 1000 * 60 * 1; // 1 minute
        // declarea Location Manager
        protected LocationManager locationManager;
    
        public GPSHelper(Context context) {
            this.context = context;
            getLocation();
        }
    
        public Location getLocation() {
            try {
                // get a reference to the system Location Manager service
                locationManager = (LocationManager) context
                        .getSystemService(LOCATION_SERVICE);
                // get GPS status
                isGPSEnabled = locationManager
                        .isProviderEnabled(LocationManager.GPS_PROVIDER);
                    // if GPS is enabled get location via GPS Services
                    if (isGPSEnabled) {
                        if (location == null) {
                            locationManager.requestLocationUpdates(
                                    LocationManager.GPS_PROVIDER,
                                    MIN_DISTANCE_CHANGE_TO_UPDATES,
                                    MIN_TIME_ELAPSED_BETWEEN_UPDATES, this);
                            if (locationManager != null) {
                                location = locationManager
                                        .getLastKnownLocation(LocationManager.GPS_PROVIDER);
                                if (location != null) {
                                    latitude = location.getLatitude();
                                    longitude = location.getLongitude();
                                }
                            }
                        }
                    }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return location;
        }
    
        public double getLatitude(){
            if(location != null){
                latitude = location.getLatitude();
            }
            return latitude;
        }
    
        public double getLongitude(){
            if(location != null){
                longitude = location.getLongitude();
            }
            return longitude;
        }
    
        @Override
        public void onLocationChanged(Location location) {
            getLocation();
        }
    
        @Override
        public void onProviderDisabled(String provider) {
        }
    
        @Override
        public void onProviderEnabled(String provider) {
        }
    
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
    
        @Override
        public IBinder onBind(Intent arg0) {
            return null;
        }
    }
  • Next, we will need an Activity to call and display the location data from the "GPSHelper.java". Add a new Activity called "GPSActivity", and add a "Button" control to its UI. The "activity_gps.xml" looks like this:
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="com.peterleow.androidux.GPSActivity">
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/where_am_I"
            android:id="@+id/btnGPS"
            android:layout_centerVertical="true"
            android:layout_centerHorizontal="true" />
    </RelativeLayout>

    The preview of "GPSActivity" should look like this (Figure 13),

    Figure 13: GPSActivity in Preview
  • Add the appropriate code to the "GPSActivity.java" to get and display the current location from the "GPSHelper" class upon the button click event as shown,

    import android.app.Activity;
    import android.os.Bundle;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.view.View;
    import android.widget.Button;
    import android.widget.Toast;
    
    public class GPSActivity extends Activity {
    
        GPSHelper gps;
    
        @Override
    
        protected void onCreate(Bundle savedInstanceState) {
    
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_gps);
            Button myLocation = (Button) findViewById(R.id.btnGPS);
    
            myLocation.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    gps = new GPSHelper(getApplicationContext());
                    double latitude = gps.getLatitude();
                    double longitude = gps.getLongitude();
                    Toast.makeText(getApplicationContext(), "Longitude: " + longitude + "\nLatitude: " + latitude, Toast.LENGTH_LONG).show();
                }
            });
        }
    
        // other code
    }
  • In order to receive location updates from "GPS_PROVIDER", you have to request user permission by declaring "ACCESS_FINE_LOCATION" permission in the "AndroidManifest.xml". In fact, the "ACCESS_FINE_LOCATION" permission is used for both "NETWORK_PROVIDER" and "GPS_PROVIDER". Another permission "ACCESS_COARSE_LOCATION" can only be used for "NETWORK_PROVIDER".

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.peterleow.androidux" >
        <application
            <!-- others  -->
        </application>
        <!-- others -->
    
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    
    </manifest>
  • Before you can test it on an AVD, you will have to create navigation from the "MainActivity". Do these:

    • In the "activity_main.xml", add a new "Button" control below the second button, and then add the "android:onClick" attribute to the new <Button> node as shown:

      <Button
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:text="@string/gps"
          android:id="@+id/button3"
          android:onClick="getGPSActivity"
          android:layout_below="@+id/button2"
          android:layout_alignParentLeft="true"
          android:layout_alignParentStart="true" />

      The preview of "MainActivity" should now look like this (Figure 14),

      Figure 14: MainActivity in Preview
    • In the "MainActivity.java", add the "getGPSActivity()" method to navigate to the "GPSActivity" when the button is clicked.

      public void getGPSActivity(View view) {
          Intent intent = new Intent(getApplicationContext(), GPSActivity.class);
          startActivity(intent);
      }
  • If you have an Android device, you can load the app and test the "GPSActivity" right away. Remember to enable location service on your device before the test. Can't I test it on an AVD. Of course, you can. But that calls for some more steps. Follow these steps:

    • Follow these links to launch the "Emulator Control" window on Android Studio: Tools > Android > Android Device Monitor > click on the tab that read "Emulator Control" > click on your AVD under the "Name" panel on the left (Figure 15).

      Figure 15: Emulator Control
    • You can emulate the sending of mock GPS location data from the Emulator Control, i.e. longitude and latitude, to your AVD!. Try it. (Figure 16)

      Figure 16: Emulate GPS data
    • Do not forget to enable location service on your AVD before the test. (Figures 17 to 18)

      Figure 17: Access Location Setting
      Figure 18: Enable Location Service

In a real location-aware app, you would have to listen for location change and update the location on a map at certain interval. This is beyond the scope of this article. However, to whet your appetite, look at this screenshot of one of my prototype apps (Figure 19).

Figure 19: A Location-aware App with Map

Sensors

Modern Android devices come with many built-in sensors that measure almost anything that is taking place in and around the device, such as movement of the device, position of the device, and the environmental conditions around the device. These sensors could be hardware-based or software-based. They are grouped into three broad categories as such:

  • Environmental sensors measure environmental variables around the device, such as temperature, pressure, illumination, and humidity.

  • Position sensors measure the physical position of the device and its proximity.

  • Motion sensors measure the acceleration forces and rotational forces along the x, y, and z axes of the device.

These sensors are capable of providing raw data with high precision and accuracy that you can use them in your app through the Android sensor framework. The Android sensor framework provides "SensorManager" and "Sensor" classes for accessing the sensors in our app.

To use the sensors , follow these steps:

  • Instantiate a "SensorManager" object like this:

    SensorManager sensorManager = (SensorManager)this.getSystemService(SENSOR_SERVICE);
  • Instantiate a "Sensor" by calling the "getDefaultSensor()" method of the "SensorManager" class and passing the sensor type as argument.

    Sensor temperature = sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);
  • Register a "SensorEventListener" with the "SensorManager" in the "onResume()" method of the Activity, and override two methods, i.e. "onAccuracyChanged()" and "onSensorChanged" as follows:

    sensorManager.registerListener(this, temperature, SensorManager.SENSOR_DELAY_NORMAL);  
    public void onAccuracyChanged(Sensor sensor, int accuracy) {}
    public void onSensorChanged(SensorEvent event) {}
  • Lastly, unregister the sensor event listener in the "onPause()" method whenever the Activity is paused to prevent draining of battery power.

    @Override
    protected void onPause() {
        super.onPause();
        // unregister the sensor to prevent battery draining
        sensorManager.unregisterListener(this);
    }

Availability of Sensors

The Android system supports many sensor types, but few Android devices have every type of sensor. For example, most Android devices would have a light sensor to control the screen brightness, but few have a thermometer. Therefore, it is important for your app to verify the type of sensors available in a device before attempting to acquire data from them. Follow these steps to create an Activity in our app to list the type of sensors available in an Android device.

  • In the current project, create a new Activity called "SensorsActivity". Its "activity_sensors.xml" contains only a "ListView".
    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/listView"
        android:padding="@dimen/activity_horizontal_margin" />
  • Add the appropriate code to the "SensorsActivity.java" to get and populate information of all the available sensors to the ListView as shown,

    package com.peterleow.androidux;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.ArrayAdapter;
    import android.widget.ListView;
    import android.hardware.Sensor;
    import android.hardware.SensorManager;
    import java.util.ArrayList;
    import java.util.List;
    
    public class SensorsActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_sensors);
    
            SensorManager sensorManager = (SensorManager)this.getSystemService(SENSOR_SERVICE);
    
            List<sensor> list = sensorManager.getSensorList(Sensor.TYPE_ALL);
    
            List<string> sensorArray = new ArrayList<string>();
    
            // extract and store each sensor information in an ArrayList
            for(Sensor sensor: list){
                String item = sensor.getName() + "\n" +
                         sensor.getVendor() + "\n" +
                           sensor.getVersion();
    
                sensorArray.add(item);
            }
    
            ArrayAdapter<string> adapter = new ArrayAdapter<string>(
                    this,
                    android.R.layout.simple_list_item_1,
                    sensorArray);
    
            ListView listView = (ListView)findViewById(R.id.listView);
            listView.setAdapter(adapter);
        }
    
        // other code
    }</string></string></string></string></sensor>
  • Before you can test it on an AVD, you will have to create navigation from the "MainActivity". Do these:

    • In the "activity_main.xml", add a new "Button" control below the second button, and then add the "android:onClick" attribute to the new <Button> node as shown:

      <Button
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:text="@string/list_of_sensors"
          android:id="@+id/button4"
          android:onClick="getSensorActivity"
          android:layout_below="@+id/button3"
          android:layout_alignParentLeft="true"
          android:layout_alignParentStart="true" />

      The preview of "MainActivity" should now look like this (Figure 20),

      Figure 20: MainActivity in Preview
    • In the "MainActivity.java", add the "getSensorActivity()" method to navigate to the "SensorsActivity" when the button is clicked.

      public void getSensorActivity(View view) {
          Intent intent = new Intent(getApplicationContext(), SensorsActivity.class);
          startActivity(intent);
      }
  • You should test your app on a real device. When you navigate to the "SensorsActivity", it should display a list of all available sensors on your device. (An example on a real device in Figure 21.)

    Figure 21: Sensors Info on a Real Device

Environment Sensors

The Android platform provides four types of hardware-based sensors to measure such environmental variables as air pressure, air temperature, light, and humidity. However, it is up to device manufacturers to decide which of these to build into their devices. Table 2 lists the four environment sensors supported by the Android system. We will create an Activity to read the pressure sensor in our app. All these sensors return a single value that can be obtained from "SensorEvent.values[0]".

Table 2: Environment Sensors
Sensor Unit Description

TYPE_LIGHT

lx

Illuminance.

TYPE_PRESSURE hPa or mbar Air pressure

TYPE_RELATIVE_HUMIDITY

%

Relative humidity

TYPE_AMBIENT_TEMPERATURE °C Air temperature.
  • In the current project, create a new Activity called "EnvironmentActivity". Its "activity_environment.xml" contains a "TextView" as shown.
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="com.peterleow.androidux.EnvironmentActivity">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Medium Text"
            android:id="@+id/textView"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="119dp" />
    </RelativeLayout>
  • Add the appropriate code to the "EnvironmentActivity.java" to read the pressure information from the pressure sensor and display it on the "TextView" as shown,

    package com.peterleow.androidux;
    
    import android.app.Activity;
    import android.hardware.Sensor;
    import android.hardware.SensorEvent;
    import android.hardware.SensorEventListener;
    import android.hardware.SensorManager;
    import android.os.Bundle;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.TextView;
    
    public class EnvironmentActivity extends Activity implements SensorEventListener {
        private SensorManager sensorManager;
        private Sensor sensor;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_environment);
    
            sensorManager = (SensorManager)this.getSystemService(SENSOR_SERVICE);
    
            sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            // Register a listener for the sensor.
            sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
        }
    
        @Override
        public final void onSensorChanged(SensorEvent event) {
            // get reading from the sensor
            float pressure = event.values[0];
            TextView textView = (TextView) findViewById(R.id.textView);
            textView.setText("Current air pressure is " + pressure + " hPa");
        }
    
        @Override
        public final void onAccuracyChanged(Sensor sensor, int accuracy) {
            // to do something
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            // unregister the sensor to prevent battery draining
            sensorManager.unregisterListener(this);
        }
    
        // other code
    }
  • Before you can test it on an AVD, you will have to create navigation from the "MainActivity". Do these:

    • The preview of "MainActivity" should now look like this (Figure 22),

      Figure 22: MainActivity in Preview
    • In the "MainActivity.java", add the "getEnvironmentActivity()" method to navigate to the "EnvironmentActivity" when the button is clicked.

          public void getEnvironmentActivity(View view) {
              Intent intent = new Intent(getApplicationContext(), EnvironmentActivity.class);
              startActivity(intent);
          }
  • You should test your app on a real device. When you navigate to the "EnvironmentActivity", it should display and update the current air pressure around your device continuously. (An example on a real device in Figure 23.)

    Figure 23: Pressure Info on a Real Device

Position Sensors

The Android platform provides two types of hardware-based position sensors -

  • The geomagnetic field sensor determines the position of a device the world's frame of reference.

  • The proximity sensor determines how close the face of a device is to an object.

The geomagnetic field sensor returns multi-dimensional arrays of values for each sensor event whereas the proximity sensor returns a single value. Table 3 lists the type of position sensors supported by the Android system. We will create an Activity to read the proximity sensor in our app.

Table 3: Position Sensors
Sensor

Unit

Description

TYPE_PROXIMITY

cm

Distance to an object obtained from "SensorEvent.values[0]"

TYPE_MAGNETIC_FIELD μT

"SensorEvent.values[0]" returns geomagnetic field strength along the x axis.

"SensorEvent.values[1]" returns geomagnetic field strength along the y axis.

"SensorEvent.values[2]" returns geomagnetic field strength along the z axis.

TYPE_MAGNETIC_FIELD_UNCALIBRATED μT

"SensorEvent.values[0]" returns geomagnetic field strength (without hard iron calibration) along the x axis.

"SensorEvent.values[1]" returns geomagnetic field strength (without hard iron calibration) along the y axis.

"SensorEvent.values[2]" returns geomagnetic field strength (without hard iron calibration) along the z axis.

"SensorEvent.values[3]" returns iron bias estimation along the x axis.

"SensorEvent.values[4]" returns iron bias estimation along the y axis.

"SensorEvent.values[5]" returns iron bias estimation along the z axis.

TYPE_GEOMAGNETIC_ROTATION_VECTOR -

"SensorEvent.values[0]" returns rotation vector component along the x axis (x * sin(θ/2)).

"SensorEvent.values[1]" returns rotation vector component along the y axis (y * sin(θ/2)).

"SensorEvent.values[2]" returns rotation vector component along the z axis (z * sin(θ/2)).

TYPE_GAME_ROTATION_VECTOR -

"SensorEvent.values[0]" returns rotation vector component along the x axis (x * sin(θ/2)).

"SensorEvent.values[1]" returns rotation vector component along the y axis (y * sin(θ/2)).

"SensorEvent.values[2]" returns rotation vector component along the z axis (z * sin(θ/2)).

  • In the current project, create a new Activity called "PositionActivity". Its "activity_position.xml" contains a "TextView" as shown.
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="com.peterleow.androidux.PositionActivity">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Medium Text"
            android:id="@+id/textView"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="119dp" />
    </RelativeLayout>
  • Add the appropriate code to the "PositionActivity.java" to read the proximity information from the proximity sensor and display it on the "TextView" as shown,

    package com.peterleow.androidux;
    
    import android.app.Activity;
    import android.hardware.Sensor;
    import android.hardware.SensorEvent;
    import android.hardware.SensorEventListener;
    import android.hardware.SensorManager;
    import android.os.Bundle;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.TextView;
    
    public class PositionActivity extends Activity implements SensorEventListener {
        private SensorManager sensorManager;
        private Sensor sensor;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_position);
    
            sensorManager = (SensorManager)this.getSystemService(SENSOR_SERVICE);
    
            sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            // Register a listener for the sensor.
            sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
        }
    
        @Override
        public final void onSensorChanged(SensorEvent event) {
            // get reading from the sensor
            float proximity = event.values[0];
            TextView textView = (TextView) findViewById(R.id.textView);
            textView.setText("Proximity is " + proximity + " cm");
        }
    
        @Override
        public final void onAccuracyChanged(Sensor sensor, int accuracy) {
            // to do something
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            // unregister the sensor to prevent battery draining
            sensorManager.unregisterListener(this);
        }
    
        // other code
    }
  • Again, you will create navigation from the "MainActivity". As you have done it many times, I shall not repeat. The preview of "MainActivity" should now look like this (Figure 24),

    Figure 24: MainActivity in Preview
  • You should test your app on a real device. When you navigate to the "PositionActivity", it should display the proximity of your device. (An example on a real device in Figure 25.)

    Figure 25: Proximity Info on a Real Device

Motion Sensors

The Android platform provides a number of motion sensors that your app can use to monitor the movement of a device such as such as tilt, shake, rotation, or swing. Two of these sensors, i.e. accelerometer and gyroscope, are always hardware-based, while the other three, i.e. gravity, linear acceleration, and rotation vector sensors can be either hardware-based or software-based. All these sensors return multi-dimensional arrays of values for each sensor event. Table 4 lists the type of motion sensors supported by the Android system. We will create an Activity to read the TYPE_ACCELEROMETER sensor in our app.

Table 4: Motion Sensors
Sensor

Unit

Description

TYPE_ACCELEROMETER

m/s2

"SensorEvent.values[0]" returns acceleration force along the x axis with gravity.

"SensorEvent.values[1]" returns acceleration force along the y axis with gravity.

"SensorEvent.values[2]" returns acceleration force along the z axis with gravity.

TYPE_LINEAR_ACCELERATION m/s2

"SensorEvent.values[0]" returns acceleration force along the x axis without gravity.

"SensorEvent.values[1]" returns acceleration force along the y axis without gravity.

"SensorEvent.values[2]" returns acceleration force along the z axis without gravity.

TYPE_GRAVITY m/s2

"SensorEvent.values[0]" returns gravitational force along the x axis.

"SensorEvent.values[1]" returns gravitational force along the y axis.

"SensorEvent.values[2]" returns gravitational force along the z axis.

TYPE_GYROSCOPE rad/s

"SensorEvent.values[0]" returns rate of rotation around the x axis.

"SensorEvent.values[1]" returns rate of rotation around the y axis.

"SensorEvent.values[2]"returns rate of rotation around the z axis.

TYPE_GYROSCOPE_UNCALIBRATED rad/s

"SensorEvent.values[0]" returns rate of rotation (without drift compensation) around the x axis.

"SensorEvent.values[1]" returns rate of rotation (without drift compensation) around the y axis.

"SensorEvent.values[2]" returns rate of rotation (without drift compensation) around the z axis.

"SensorEvent.values[3]" returns estimated drift around the x axis.

"SensorEvent.values[4]" returns estimated drift around the y axis.

"SensorEvent.values[5]" returns estimated drift around the z axis.

TYPE_ROTATION_VECTOR -

"SensorEvent.values[0]" returns rotation vector component along the x axis (x * sin(θ/2)).

"SensorEvent.values[1]" returns rotation vector component along the y axis (y * sin(θ/2)).

"SensorEvent.values[2]"returns rotation vector component along the z axis (z * sin(θ/2)).

"SensorEvent.values[3]"returns scalar component of the rotation vector (cos(θ/2)). This is optional.

  • In the current project, create a new Activity called "MotionActivity". Its "activity_motion.xml" contains a "TextView" as shown.
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="com.peterleow.androidux.MotionActivity">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Medium Text"
            android:id="@+id/textView"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="119dp" />
    </RelativeLayout>
  • Add the appropriate code to the "MotionActivity.java" to read the acceleration information from the TYPE_ACCELEROMETER sensor and display it on the "TextView" as shown,

    package com.peterleow.androidux;
    
    import android.app.Activity;
    import android.hardware.Sensor;
    import android.hardware.SensorEvent;
    import android.hardware.SensorEventListener;
    import android.hardware.SensorManager;
    import android.os.Bundle;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.TextView;
    
    public class MotionActivity extends Activity implements SensorEventListener {
        private SensorManager sensorManager;
        private Sensor sensor;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_motion);
    
            sensorManager = (SensorManager)this.getSystemService(SENSOR_SERVICE);
    
            sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            // Register a listener for the sensor.
            sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
        }
    
        @Override
        public final void onSensorChanged(SensorEvent event) {
            // get reading from the sensor
            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];
            TextView textView = (TextView) findViewById(R.id.textView);
            textView.setText("Acceleration along \nx axis: " + x + " m/s(2)\n" +
                                    "y axis: " + y +  " m/s(2)\n" +
                                    "z axis: " + z +  " m/s(2)\n");
        }
    
        @Override
        public final void onAccuracyChanged(Sensor sensor, int accuracy) {
            // to do something
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            // unregister the sensor to prevent battery draining
            sensorManager.unregisterListener(this);
        }
    
        // other code
    }
  • Again, you will create navigation from the "MainActivity". The preview of "MainActivity" should now look like this (Figure 26),

    Figure 26: MainActivity in Preview
  • Test your app on a real device. When you navigate to "MotionActivity", it should display and update the acceleration along the x, y, and z axis of your device continuously. (An example on a real device in Figure 27.)

    Figure 27: Acceleration info on a Real Device

Best Practices

As we approach the rest stop, I have put together some best practices that will help you better implement the various Android interactivity features to enhance the UX of your Android app.

Acknowledging Gestures

No one wants to be kept in the dark. Always keep the users informed of the state, progress, and the expected outcome of the interactions that they are engaging in on their devices. Measures like a change of screen illumination and change of state of View upon each gesture will provide subtle and informative visual clue to the users.

Smart Keyboard

To overcome the constraint of screen size on any Android device, we will have to present the users with the type of keyboard that is most appropriate to the type of input that they are expected to enter. Additional help like auto correct will be greatly appreciated by the users. Incorporating smart keyboard into your app is the sure way to improve productivity and efficiency on keyboarding on small screen devices.

Knowing Your Location

Knowing where the user is allows your app to be smarter and deliver better information and services to the user. For example, based on the GPS location data, you app can deliver the most relevant and timely information on weather, traffic condition, places of interest, amenities, direction, and many more.

Tips

Managing Trade-off

While incorporating location-aware function in your app is great, but frequent update quickly consumes battery power and takes up too much resources. There is a need to manage the trade-off between accuracy of location data and the frequency of update. In designing your location-aware app, you should choose the degree of accuracy as well as the retention period of old location data that are most appropriate for the purpose of the app by setting the minimum time interval between location updates and the minimum distance between location updates. This can be achieved by setting the "minTime" and the "minDistance" parameters of the "requestLocationUpdates()" method of the "LocationManager" class. For example, if your application is mainly for finding points of interest in your vicinity, you probably do not need high accuracy and the location information do not have to be updated so often. On the other hand, if your application is to provide turn by turn navigation direction for drivers, then high accuracy and constant update of location become necessary.

Becoming Sensible

Always turn off the sensor in your app when you do not need it so as not to drain battery power unnecessarily. This can be done automatically by unregistering the sensor event listener in the "onPause()" callback method of the Activity.

Summary

This journey has led you to visit and explore in first-hand the various user interactivity features and sensors of the Android system as follows:

These interactivity features and sensors when used innovatively and sensibly will greatly enhance the UX of your app. So start using them wisely in your app. See you next time.

Reference

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Peter Leow
Instructor / Trainer
Singapore Singapore
Graduated from the National University of Singapore with the following qualifications:
1. Graduate Diploma in Systems Analysis (2002)
2. Master of Technology (Knowledge Engineering) (2013)
 
Currently, lecturing in the area of social media and web development at a technical institution.
 
Having hibernated for ages, finally woke up to serve the Community in Oct 2013.
Follow on   LinkedIn

Comments and Discussions

 
QuestionSwiping between Activities PinmemberBRShroyer15-Sep-14 5:18 
AnswerRe: Swiping between Activities PinprofessionalPeter Leow15-Sep-14 6:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 25 Aug 2014
Article Copyright 2014 by Peter Leow
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid