Mar 30, 2013

Android: Detect Global Touch Event

Standard
Sidebar and Glovebox Apps became popular overnight because of only one functionality. They provide global menu which can be opened from left or right edge of the mobile screen.  As a developer, when I show these apps, first thing that comes in my mind was "NDK".  Then, I started looking for the solution.  Logically, I figured out that if I can detect "Global onTouch Event" then it's posible to display side menu like sidebar or glovebox.


 


https://play.google.com/store/apps/details?id=com.fb.glovebox
https://play.google.com/store/apps/details?id=mohammad.adib.sidebar.lite

I was in search of detecting global onTouch event in Android and finally I got success because of Mr. YoungBin Han. He presented one technique to detect global touch event through sidepanel application. In this article, I am explaining simplified code of detecting global event in Android.

Problem 1: Detect Global Touch Event 
Problem 2: Display Sliding Menu.

Solution For Problem 1: 
1. Create service to detect touch event.
2. Fetch WindowManager object from system service
3. Create LinearLayout having 30 px width and fill_parent height
4. Add LinearLayout to WindowManager
5. Set LinearLayout on left edge of screen / window.

Q1 : Why I need to create Service ? 
We can't get event when activity goes in background or closes.

Q2 : What if I make LinearLayout width more then 30 px ?
The control or activity which displays in background will not get onTouch event.

Cyan color is used to make LinearLayout visible.

Let's jump to the code.

Step 1 : Create android project named "Global Touch Event"
Step 2 : Open activity_main.xml and add button to start the service.
<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"
    tools:context=".MainActivity" >
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="Start Touch Detection"
        android:onClick="buttonClicked" />
</RelativeLayout>
Step 3 : Open MainActivity.java and add buttonClicked event to start service.
package com.kpbird.globaltouchevent;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {

 Intent globalService;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  globalService = new Intent(this,GlobalTouchService.class);
 }


 public void buttonClicked(View v){
  
  if(v.getTag() == null){
   startService(globalService);
   v.setTag("on");
   Toast.makeText(this, "Start Service", Toast.LENGTH_SHORT).show();
  }
  else{
   stopService(globalService);
   v.setTag(null);
   Toast.makeText(this, "Stop Service", Toast.LENGTH_SHORT).show();
  }
  
 }
}

Step 4 : Create new java class named "GlobalTouchService" which extends Service and implements OnTouchListener. Now create LinearLayout and add it into WindowManager, when user touch on left edge of screen this LinearLayout will detect event. If you want to view LinearLayout just set any color in background or uncomment setBackgroundColor line in following source code.
package com.kpbird.globaltouchevent;

import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;

public class GlobalTouchService extends Service implements OnTouchListener{

 private String TAG = this.getClass().getSimpleName();
 // window manager 
 private WindowManager mWindowManager;
 // linear layout will use to detect touch event
 private LinearLayout touchLayout;
 @Override
 public IBinder onBind(Intent arg0) {
  return null;
 }
 @Override
 public void onCreate() {
  super.onCreate();
  // create linear layout
  touchLayout = new LinearLayout(this);
  // set layout width 30 px and height is equal to full screen
  LayoutParams lp = new LayoutParams(30, LayoutParams.MATCH_PARENT);
  touchLayout.setLayoutParams(lp);
  // set color if you want layout visible on screen
//  touchLayout.setBackgroundColor(Color.CYAN); 
  // set on touch listener
  touchLayout.setOnTouchListener(this);

  // fetch window manager object 
   mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
   // set layout parameter of window manager
   WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
           30, // width of layout 30 px
           WindowManager.LayoutParams.MATCH_PARENT, // height is equal to full screen
                 WindowManager.LayoutParams.TYPE_PHONE, // Type Phone, These are non-application windows providing user interaction with the phone (in particular incoming calls).
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // this window won't ever get key input focus  
                 PixelFormat.TRANSLUCENT);      
         mParams.gravity = Gravity.LEFT | Gravity.TOP;   
   Log.i(TAG, "add View");

      mWindowManager.addView(touchLayout, mParams);
  
 }
 

 @Override
 public void onDestroy() {
   if(mWindowManager != null) {
             if(touchLayout != null) mWindowManager.removeView(touchLayout);
         }
  super.onDestroy();
 }
 
 @Override
 public boolean onTouch(View v, MotionEvent event) {
  if(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_UP)
   Log.i(TAG, "Action :" + event.getAction() + "\t X :" + event.getRawX() + "\t Y :"+ event.getRawY());
  
  return true;
 }

}

Step 5 : Open AndroidManifest.xml and register "GlobalTouchService", We also require SYSTEM_ALERT_WINDOW permission to add view in windowmanager.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.kpbird.globaltouchevent"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="GlobalTouchService" >
        </service>
    </application>
</manifest>

Step 6 : Ready to run the app.

Download or Fork Code from GitHub
I will address Problem 2 in my next article.

References
1. http://developer.android.com/reference/android/view/WindowManager.html 
2. http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html
3. https://github.com/sukso96100/SidePanel
4. http://stackoverflow.com/questions/6714020/how-can-a-service-listen-for-touch-gestures-events

6 comments:

  1. Anatoly Korniltsev12:58 AM

    really? hardcoded pixels?

    ReplyDelete
  2. kpbird7:55 PM

    Hello Anatoly, This code is just for reference, I want to show the logic behind slideout menu.

    ReplyDelete
  3. Rohit Dodle6:12 PM

    Hello brother,


    I am Ro-HIT. I am an intermediate java programmer. I am participating in a competition @ my campus level. It is for android os. I have a good project idea. For that, I need my app to pop up when the user makes a loop on touch sensitive screen. Is it possible ? with the example you gave abov ?? Please help me out. It would be great if you can mail me. (rohitd.iiitb@gmail.com). Thanks brother.

    ReplyDelete
  4. not working in 4.2.2 :(

    ReplyDelete
  5. Ashish Sahu9:59 PM

    Sir your git just not working please check your code and upload it again thanks

    ReplyDelete