How to check if application is system app or not (By signed signature)


This is my utility class to check if application is system app or not.
This checks if application is signed by system signature or not.
So this is very accurate logic.

	
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;

public class AppUtility {

    private static final String SYSTEM_PACKAGE_NAME = "android";
    private PackageManager mPackageManager = null;

    public AppUtility(Context context) {
        mPackageManager = (PackageManager) context.getPackageManager();
    }

    /**
     * Match signature of application to identify that if it is signed by system
     * or not.
     * 
     * @param packageName
     *            package of application. Can not be blank.
     * @return <code>true</code> if application is signed by system certificate,
     *         otherwise <code>false</code>
     */
    public boolean isSystemApp(String packageName) {
        try {
            // Get packageinfo for target application
            PackageInfo targetPkgInfo = mPackageManager.getPackageInfo(
                    packageName, PackageManager.GET_SIGNATURES);
            // Get packageinfo for system package
            PackageInfo sys = mPackageManager.getPackageInfo(
                    SYSTEM_PACKAGE_NAME, PackageManager.GET_SIGNATURES);
            // Match both packageinfo for there signatures
            return (targetPkgInfo != null && targetPkgInfo.signatures != null && sys.signatures[0]
                    .equals(targetPkgInfo.signatures[0]));
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    /**
     * Check if application is preloaded. It also check if the application is
     * signed by system certificate or not.
     * 
     * @param packageName
     *            package name of application. Can not be null.
     * @return <code>true</code> if package is preloaded and system.
     */
    public boolean isAppPreLoaded(String packageName) {
        if (packageName == null) {
            throw new IllegalArgumentException("Package name can not be null");
        }
        try {
            ApplicationInfo ai = mPackageManager.getApplicationInfo(
                    packageName, 0);
            // First check if it is preloaded.
            // If yes then check if it is System app or not.
            if (ai != null
                    && (ai.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) {
                // Check if signature matches
                if (isSystemApp(packageName) == true) {
                    return true;
                } else {
                    return false;
                }
            }
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }
}

I would like your comments after using these methods and if any suggestion you have.

Some stuffs with .apk files while RnD.

October 18, 2013 Leave a comment

Here are some useful steps/ commands to do RnD with .apk file

Where are apks files into device

  1. System apk’s are stored at
  2. /system/app/
  3. 3rd Party apk’s are stored at
  4. /data/app/
Programatically you can get path as below

PackageManager  pm = getPackageManager();
List<PackageInfo> pkginfolist = pm.getInstalledPackages(PackageManager.GET_ACTIVITIES);
List<ApplicationInfo> appinfoList = pm.getInstalledApplications(0);
for (int x = 0; x < pkginfolist .size(); x++) {             
  PackageInfo pkginfo = pkginfolist.get(x);
  pkg_path[x] = appinfoList.get(x).publicSourceDir;  
}
From command line you can get path as below

  1. Determine the package name of the app, e.g. "com.myapp.myhelloapp". Skip this step if you already know the package name.
  2. adb shell pm list packages
    

    Look through the list of package names and try to find a match between the app in question and the package name. This is usually easy, but note that the package name can be completely unrelated to the app name. If you can’t recognize the app from the list of package names, try finding the app in Google Play using a browser. The URL for an app in Google Play contains the package name.

  3. Get the full path name of the APK file for the desired package.
  4. adb shell pm path com.myapp.myhelloapp
    

    the output will be: package:/data/app/com.myapp.myhelloapp-2.apk

Pull the APK file from the Android device to the development box. 

adb pull /system/app/APK_FILE_NAME.apk //OR
adb pull /data/app/APK_FILE_NAME.apk

as above we found path of apk file path /data/app/com.myapp.myhelloapp-2.apk , so we can use as

adb pull /data/app/com.myapp.myhelloapp-2.apk
Get package name of application from apk file.

  1. All information from manifest
  2. aapt dump badging <path-to-apk>
    
  3. Get only package name and main Activity
    • Create applicationInfo.sh file with below data
    • package=`aapt dump badging $* | grep package | awk '{print $2}' | sed s/name=//g | sed s/\'//g`
      activity=`aapt dump badging $* | grep Activity | awk '{print $2}' | sed s/name=//g | sed s/\'//g`
      echo
      echo package : $package
      echo activity: $activity
      
    • execute command as
    • applicationInfo.sh <path-to-apk>
      

      This will print package name and main activity. You can modify .sh file as per your need.

Capture Toast or Notification using Accessibility Service


I did a sample application to Capture Toast/ Notification raised by another/ same application via Accessibility Service.
Here our Service will capture the Toast/ Notification and send Broadcast with text (added into Toast/ NOtification) and source package.

Add Permission to manifest
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />

Add Service to manifest
<service android:name=".MyAccessibilityService"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>
</service>

And Here is Our Service
import java.util.List;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.TargetApi;
import android.app.Notification;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Parcelable;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;

/**
 * This service class catches Toast or Notification of applications
 * 
 * @author pankaj
 */
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class MyAccessibilityService extends AccessibilityService {

	private final AccessibilityServiceInfo info = new AccessibilityServiceInfo();
	private static final String TAG = "MyAccessibilityService";	
	
	@Override
	public void onAccessibilityEvent(AccessibilityEvent event) {
		
		final int eventType = event.getEventType(); 
		if (eventType == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
			final String sourcePackageName = (String)event.getPackageName();
		    Parcelable parcelable = event.getParcelableData();
		    
		    if (parcelable instanceof Notification) {
		    	// Statusbar Notification
		    	
		    	//Notification notification = (Notification) parcelable; 
		    	//Log.e(TAG, "Notification -> notification.tickerText :: " + notification.tickerText);
		    	List<CharSequence> messages = event.getText();
		    	if (messages.size() > 0) {
			    	final String notificationMsg = (String) messages.get(0);	  			    	
			    	Log.v(TAG, "Captured notification message [" + notificationMsg + "] for source [" + sourcePackageName + "]");				    
				    Log.v(TAG, "Broadcasting for " + Constants.ACTION_CATCH_NOTIFICATION);
				    try {
				    	Intent mIntent = new Intent(Constants.ACTION_CATCH_NOTIFICATION);
					    mIntent.putExtra(Constants.EXTRA_PACKAGE, sourcePackageName);
					    mIntent.putExtra(Constants.EXTRA_MESSAGE, notificationMsg);
					    MyAccessibilityService.this.getApplicationContext().sendBroadcast(mIntent);
				    } catch (Exception e) {
				    	Log.e(TAG, e.toString());
				    }
		    	} else {
		    		Log.e(TAG, "Notification Message is empty. Can not broadcast"); 
		    	}
		    } else {
		        // Something else, e.g. a Toast message
		    	// Read message and broadcast
		    	List<CharSequence> messages = event.getText();
		    	if (messages.size() > 0) {
			    	final String toastMsg = (String) messages.get(0);	  			    	
			    	Log.v(TAG, "Captured message [" + toastMsg + "] for source [" + sourcePackageName + "]");				    
				    Log.v(TAG, "Broadcasting for " + Constants.ACTION_CATCH_TOAST);
				    try {
				    	Intent mIntent = new Intent(Constants.ACTION_CATCH_TOAST);
					    mIntent.putExtra(Constants.EXTRA_PACKAGE, sourcePackageName);
					    mIntent.putExtra(Constants.EXTRA_MESSAGE, toastMsg);
					    MyAccessibilityService.this.getApplicationContext().sendBroadcast(mIntent);
				    } catch (Exception e) {
				    	 Log.v(TAG, e.toString());
				    }
		    	} else {
		    		Log.e(TAG, "Message is empty. Can not broadcast"); 
		    	}
		    }
		} else {
			Log.v(TAG, "Got un-handled Event"); 
		}
	}

	@Override
	public void onInterrupt() {
		
	}
	
	@Override
	public void onServiceConnected() {
	    // Set the type of events that this service wants to listen to.  
		//Others won't be passed to this service.
	    info.eventTypes = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;

	    // If you only want this service to work with specific applications, set their
	    // package names here.  Otherwise, when the service is activated, it will listen
	    // to events from all applications.
	    //info.packageNames = new String[]
	            //{"com.appone.totest.accessibility", "com.apptwo.totest.accessibility"};

	    // Set the type of feedback your service will provide.
	    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
	    	info.feedbackType = AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
	    } else {
	    	info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
	    }	    

	    // Default services are invoked only if no package-specific ones are present
	    // for the type of AccessibilityEvent generated.  This service *is*
	    // application-specific, so the flag isn't necessary.  If this was a
	    // general-purpose service, it would be worth considering setting the
	    // DEFAULT flag.

	    // info.flags = AccessibilityServiceInfo.DEFAULT;

	    info.notificationTimeout = 100;

	    this.setServiceInfo(info);
	}

	public static final class Constants {
		
		public static final String EXTRA_MESSAGE = "extra_message"; 
		public static final String EXTRA_PACKAGE = "extra_package";
		public static final String ACTION_CATCH_TOAST = "com.mytest.accessibility.CATCH_TOAST";	
		public static final String ACTION_CATCH_NOTIFICATION = "com.mytest.accessibility.CATCH_NOTIFICATION";	
	}
	
	/**
	 * Check if Accessibility Service is enabled. 
	 *  
	 * @param mContext
	 * @return <code>true</code> if Accessibility Service is ON, otherwise <code>false</code>
	 */
	public static boolean isAccessibilitySettingsOn(Context mContext) {
		int accessibilityEnabled = 0;
		final String service = "com.mytest.accessibility/com.mytest.accessibility.MyAccessibilityService";
		
		boolean accessibilityFound = false;
		try {
			accessibilityEnabled = Settings.Secure.getInt(
					mContext.getApplicationContext().getContentResolver(),
					android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
			Log.v(TAG, "accessibilityEnabled = " + accessibilityEnabled);
		} catch (SettingNotFoundException e) {
			Log.e(TAG, "Error finding setting, default accessibility to not found: "
							+ e.getMessage());
		}
		TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');

		if (accessibilityEnabled == 1) {
			Log.v(TAG, "***ACCESSIBILIY IS ENABLED*** -----------------");
			String settingValue = Settings.Secure.getString(
					mContext.getApplicationContext().getContentResolver(),
					Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
			if (settingValue != null) {
				TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
				splitter.setString(settingValue);
				while (splitter.hasNext()) {
					String accessabilityService = splitter.next();
					
					Log.v(TAG, "-------------- > accessabilityService :: " + accessabilityService);
					if (accessabilityService.equalsIgnoreCase(service)) {
						Log.v(TAG, "We've found the correct setting - accessibility is switched on!");
						return true;
					}
				}
			}
		} else {
			Log.v(TAG, "***ACCESSIBILIY IS DISABLED***");
		}
				
		return accessibilityFound;		
	}
}

Now we will write a receiver to listen Broadcast 
I used an Activity and register a Receiver. You can do another ways also.
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;

import com.mytest.accessibility.MyAccessibilityService.Constants;

public class ToastOrNotificationTestActivity extends Activity {

	private static final String TAG = "ToastOrNotificationTestActivity";
   	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		final IntentFilter mIntentFilter = new IntentFilter(Constants.ACTION_CATCH_NOTIFICATION); 
		mIntentFilter.addAction(Constants.ACTION_CATCH_TOAST);
        registerReceiver(toastOrNotificationCatcherReceiver, mIntentFilter);
		Log.v(TAG, "Receiver registered.");
	}
			
	@Override
	protected void onDestroy() {
		super.onDestroy();
		unregisterReceiver(toastOrNotificationCatcherReceiver);
	}
		
	private final BroadcastReceiver toastOrNotificationCatcherReceiver = new BroadcastReceiver() {
		
        @Override
        public void onReceive(Context context, Intent intent) {
        	Log.v(TAG, "Received message"); 
        	Log.v(TAG, "intent.getAction() :: " + intent.getAction());
        	Log.v(TAG, "intent.getStringExtra(Constants.EXTRA_PACKAGE) :: " + intent.getStringExtra(Constants.EXTRA_PACKAGE));
        	Log.v(TAG, "intent.getStringExtra(Constants.EXTRA_MESSAGE) :: " + intent.getStringExtra(Constants.EXTRA_MESSAGE));        	
        }
    };
}

We did from code. Run the application and goto

home -> Settings -> accessibility and on the MyAccessibilityService. After this step our app is ready to capture Toast/ Notification
raised by given (or all) application.

One more thing, you can create Service into one more way. That is using xml and meta tag into manifest. Hope I will work on that and update.

Example of WebView with result handler and connection timeout handler..

January 9, 2013 4 comments

I am going to discuss about how to handle results if url called via 
webview, and another MOST IMPORTANT task is to handle connection timeout.

Here we GO…

web_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <WebView
        android:id="@+id/my_webkit"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="URL"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="Enter URL for test timeout or result"
        android:inputType="textUri" >

        <requestFocus />
    </EditText>

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Load" />

</LinearLayout>

MainActivity.java

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.URLUtil;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity {

	private static final int REQUEST_CODE = 1000;
	private Button mButtonUrl = null; 
	private EditText mEditTextUrl = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);		
		
		initComponents(); 
		addListener();		
	}
	
	
	private void initComponents() {
		mButtonUrl = (Button) findViewById(R.id.button1);
		mEditTextUrl = (EditText) findViewById(R.id.editText1);
	}

	private void addListener() {
		mButtonUrl.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				
				//Validate URL
				if (TextUtils.isEmpty(mEditTextUrl.getText())) {
					mEditTextUrl.setError("Server URL can not be empty...");
				} else {
					if (URLUtil.isValidUrl(mEditTextUrl.getText().toString())) {
						WebviewActivity.callWebview(MainActivity.this, mEditTextUrl.getText().toString().trim(), REQUEST_CODE);
					} else {
						mEditTextUrl.setError("Invalid URL...");
					}
				}
			}
		});
		
		mEditTextUrl.addTextChangedListener(new TextWatcher() {
			
			@Override
			public void onTextChanged(CharSequence s, int start, int before, int count) {
				if(s != null && s.length() > 0 && mEditTextUrl.getError() != null) {
					mEditTextUrl.setError(null);
		        }
			}
			
			@Override
			public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
			
			@Override
			public void afterTextChanged(Editable s) {}
		});		
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if(requestCode == REQUEST_CODE) {
			if(resultCode == Activity.RESULT_OK) {
				//Got something from server. Read that.
				new WebviewResultReader(MainActivity.this).execute(data);
			}
		}
	}	

	public class WebviewResultReader extends AsyncTask<Intent, Void, Void> {
		private Context mContext;
		private ProgressDialog mProgressDialog;
		
		public WebviewResultReader(Context mContext) {
			this.mContext = mContext;
			this.mProgressDialog = new ProgressDialog(this.mContext);
			this.mProgressDialog.setTitle("Wait");
			this.mProgressDialog.setMessage("Reading result...");
		}
			
		@Override
		protected void onPreExecute() {
			this.mProgressDialog.show();
			
			super.onPreExecute();
		}

		@Override
		protected void onPostExecute(Void result) {
			if (this.mProgressDialog != null) 
				this.mProgressDialog.dismiss(); 
		}

		@Override
		protected Void doInBackground(Intent... intents) {
			Uri uri = intents[0].getData();
			//TODO write your logic here to read data came from webview. 
			
			Log.i("MY_TAG", "Got response from server : \n" + uri.toString());
			return null;
		}		
	}
}

WebviewActivity.java

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.format.Time;
import android.util.Log;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

@SuppressLint("SetJavaScriptEnabled")
public class WebviewActivity extends Activity {

	private WebView browser = null;
	private static final String TAG = "WebviewActivity";
	private String requestedURL = null;
	private ConnectionTimeoutHandler timeoutHandler = null;	
	private static int PAGE_LOAD_PROGRESS = 0;
	public static final String KEY_REQUESTED_URL = "requested_url"; 
	/**
	 * A string which will be added in url if server want to send something to client after everything has been done. 
	 * 
	 * When you load any url to webview and after task done it doen't go back to caller activity. User must have to press back key. 
	 * 
	 * So what we do, we will check url each time for a string if loading url contains the string, we assume that we got result. 
	 * 
	 * In my example my server returns success string into url. 
	 * 
	 * for example: 
	 * 
	 * I called http://192.../test, after doing some task on server, server moves to http://192.../test/success, so 
	 * I read this string and say my code that I got result, move me back.  
	 */
	public static final String CALLBACK_URL = "success";
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.web_view);
        initComponents();
        addListener();
        
        requestedURL = getIntent().getStringExtra(KEY_REQUESTED_URL);
        if (requestedURL != null) {      	
        	
        	browser.loadUrl(requestedURL);
        } else {
        	Log.e(TAG, "Can not process ... URL missing.");
        }        
    }
	
	public void initComponents() {
		browser = (WebView) findViewById(R.id.webkit);
		WebSettings webSettings = browser.getSettings();
		webSettings.setJavaScriptEnabled(true);
	}

	public void addListener() {
		browser.setWebViewClient(new MyWebViewClient());
		browser.setWebChromeClient(new MyWebChromeClient());
	}

	//Custom web view client
	public class MyWebViewClient extends WebViewClient {

		@Override
		public boolean shouldOverrideUrlLoading(WebView view, String url) {
			Log.i(TAG, "Loading url : " + url);
			if (url.contains(CALLBACK_URL)) {
				Log.i(TAG, "Callback url matched... Handling the result");
				Intent intent = new Intent();
				Uri uri = Uri.parse(url);
				intent.setData(uri); 
				setResult(RESULT_OK, intent);
				finish();
				return true;
			}
			return false;
		}
	
		@Override
		public void onPageStarted(WebView view, String url, Bitmap favicon) {
			
			timeoutHandler = new ConnectionTimeoutHandler(WebviewActivity.this, view);
			timeoutHandler.execute();
			super.onPageStarted(view, url, favicon);
		}

		@Override
		public void onPageFinished(WebView view, String url) {
			
			super.onPageFinished(view, url);
			
			if (timeoutHandler != null)
				timeoutHandler.cancel(true);
			
			Log.i(TAG, "Loading url : " + url);
			// Do all your result processing here
			if (url.contains(CALLBACK_URL)) {
				Log.i(TAG, "Callback url matched... Handle result");			
				Intent mIntent = new Intent();
				Uri uri = Uri.parse(url);
				mIntent.setData(uri); 
				setResult(RESULT_OK, mIntent);
				finish();
			}
		}

		@Override
		public void onReceivedError(WebView view, int errorCode,
				String description, String failingUrl) {
			
			Log.i(TAG, "GOT Page error : code : " + errorCode + " Desc : " + description);
			showError(WebviewActivity.this, errorCode);
			//TODO We can show customized HTML page when page not found/ or server not found error. 
			super.onReceivedError(view, errorCode, description, failingUrl);
		}		
	}
	
	//Custom web chrome client
	public class MyWebChromeClient extends WebChromeClient {

		@Override
		public void onProgressChanged(WebView view, int newProgress) {
			PAGE_LOAD_PROGRESS = newProgress;
			//Log.i(TAG, "Page progress [" + PAGE_LOAD_PROGRESS + "%]");
			super.onProgressChanged(view, newProgress);
		}		
	}
	
	private void showError(Context mContext, int errorCode) {
		//Prepare message
		String message = null; 
		String title = null;
		if (errorCode == WebViewClient.ERROR_AUTHENTICATION) {
			message = "User authentication failed on server";
			title = "Auth Error";
		} else if (errorCode == WebViewClient.ERROR_TIMEOUT) {
			message = "The server is taking too much time to communicate. Try again later.";
			title = "Connection Timeout";
		} else if (errorCode == WebViewClient.ERROR_TOO_MANY_REQUESTS) {
			message = "Too many requests during this load";
			title = "Too Many Requests";
		} else if (errorCode == WebViewClient.ERROR_UNKNOWN) {
			message = "Generic error";
			title = "Unknown Error";
		} else if (errorCode == WebViewClient.ERROR_BAD_URL) {
			message = "Check entered URL..";
			title = "Malformed URL";
		} else if (errorCode == WebViewClient.ERROR_CONNECT) {
			message = "Failed to connect to the server";
			title = "Connection";
		} else if (errorCode == WebViewClient.ERROR_FAILED_SSL_HANDSHAKE) {
			message = "Failed to perform SSL handshake";
			title = "SSL Handshake Failed";
		} else if (errorCode == WebViewClient.ERROR_HOST_LOOKUP) {
			message = "Server or proxy hostname lookup failed";
			title = "Host Lookup Error";
		} else if (errorCode == WebViewClient.ERROR_PROXY_AUTHENTICATION) {
			message = "User authentication failed on proxy";
			title = "Proxy Auth Error";
		} else if (errorCode == WebViewClient.ERROR_REDIRECT_LOOP) {
			message = "Too many redirects";
			title = "Redirect Loop Error";
		} else if (errorCode == WebViewClient.ERROR_UNSUPPORTED_AUTH_SCHEME) {
			message = "Unsupported authentication scheme (not basic or digest)";
			title = "Auth Scheme Error";
		} else if (errorCode == WebViewClient.ERROR_UNSUPPORTED_SCHEME) {
			message = "Unsupported URI scheme";
			title = "URI Scheme Error";
		} else if (errorCode == WebViewClient.ERROR_FILE) {
			message = "Generic file error";
			title = "File";
		} else if (errorCode == WebViewClient.ERROR_FILE_NOT_FOUND) {
			message = "File not found";
			title = "File";
		} else if (errorCode == WebViewClient.ERROR_IO) {
			message = "The server failed to communicate. Try again later.";
			title = "IO Error";
		}
		
		if (message != null) {
			new AlertDialog.Builder(mContext)
			.setMessage(message)
			.setTitle(title)
			.setIcon(android.R.drawable.ic_dialog_alert)
			.setCancelable(false)
			.setPositiveButton("OK",
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int id) {
							setResult(RESULT_CANCELED);
							finish();
						}
					}).show();
		}		
	}
	
	public class ConnectionTimeoutHandler extends AsyncTask<Void, Void, String> {
		
		private static final String PAGE_LOADED = "PAGE_LOADED";
		private static final String CONNECTION_TIMEOUT = "CONNECTION_TIMEOUT";
		private static final long CONNECTION_TIMEOUT_UNIT = 60000L; //1 minute
		
		private Context mContext = null;
		private WebView webView;  
		private Time startTime = new Time();
		private Time currentTime = new Time();
		private Boolean loaded = false;
				
		public ConnectionTimeoutHandler(Context mContext, WebView webView) {
			this.mContext = mContext;
			this.webView = webView;
		}
			
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			this.startTime.setToNow();
			WebviewActivity.PAGE_LOAD_PROGRESS = 0;
		}

		@Override
		protected void onPostExecute(String result) {
			
			if (CONNECTION_TIMEOUT.equalsIgnoreCase(result)) {
				showError(this.mContext, WebViewClient.ERROR_TIMEOUT);
				
				this.webView.stopLoading();
			} else if (PAGE_LOADED.equalsIgnoreCase(result)) {
				//Toast.makeText(this.mContext, "Page load success", Toast.LENGTH_LONG).show();				
			} else {
				//Handle unknown events here
			}
		}

		@Override
		protected String doInBackground(Void... params) {
			
			while (! loaded) {
				currentTime.setToNow();
				if (WebviewActivity.PAGE_LOAD_PROGRESS != 100
						&& (currentTime.toMillis(true) - startTime.toMillis(true)) > CONNECTION_TIMEOUT_UNIT) {
					return CONNECTION_TIMEOUT;
				} else if (WebviewActivity.PAGE_LOAD_PROGRESS == 100) {
					loaded = true; 
				}
			}				
			return PAGE_LOADED;
		}	
	}
	
	public static void callWebview(Activity activity, String requestedURL, int requestCode) {
		
		Intent intent = new Intent(activity, WebviewActivity.class);
        intent.putExtra(WebviewActivity.KEY_REQUESTED_URL, requestedURL);
        activity.startActivityForResult(intent, requestCode);
    }
}


We did it…….

Happy coading ...

Convert/ Cast Object into BigDecimal or BigInteger.

October 10, 2012 Leave a comment

Here is my small stuffs about casting…

Object to BigDecimal

public static BigDecimal getBigDecimal(Object value) {
	BigDecimal result = new BigDecimal(0);
	if(value != null) {
		try {
			if(value instanceof BigDecimal) {
				result = (BigDecimal) value;
			} else if(value instanceof String) {
				result = new BigDecimal((String) value);
			} else if(value instanceof BigInteger) {
				result = new BigDecimal((BigInteger) value);
			} else if(value instanceof Number) {
				result = new BigDecimal(((Number) value).doubleValue());
			} else {
				//throw new ClassCastException("Not possible to coerce [" + value + "] from class " + value.getClass() + " into a BigDecimal.");
				System.out.println("Not possible to coerce [" + value + "] from class " + value.getClass() + " into a BigDecimal.");
			}
		} catch (NumberFormatException e) {
			System.out.println("Not possible to coerce [" + value + "] from class " + value.getClass() + " into a BigDecimal. " + e);
		} catch (ClassCastException e) {
			System.out.println("Not possible to coerce [" + value + "] from class " + value.getClass() + " into a BigDecimal. " + e);
		} catch (Exception e) {
			System.out.println("Exception caught. " + e);
		}			
	}
	return result;
}
Object to BigInteger

	
public static BigInteger getBigInteger(Object value) {
	BigInteger result = new BigInteger("0");
	if(value != null) {
		try {
			if(value instanceof BigInteger) {
				result = (BigInteger) value;
			} else if(value instanceof String) {
				result = new BigInteger((String) value);
			} else if(value instanceof Number) {
				result = new BigInteger(String.valueOf(((Number) value)));
			} else {
				//throw new ClassCastException("Not possible to coerce [" + value	+ "] from class " + value.getClass() + " into a BigInteger.");
				System.out.println("Not possible to coerce [" + value	+ "] from class " + value.getClass() + " into a BigInteger.");				
			}
		} catch (NumberFormatException e) {
			System.out.println("Not possible to coerce [" + value + "] from class " + value.getClass() + " into a BigDecimal. " + e);
		} catch (ClassCastException e) {
			System.out.println("Not possible to coerce [" + value + "] from class " + value.getClass() + " into a BigDecimal. " + e);
		} catch (Exception e) {
			System.out.println("Exception caught. " + e);
		}						
	}
	return result;
}

Android App Wireframing Tools

July 27, 2012 1 comment

Here is the best wireframe sketcher..


Pencil is one of my favorite wireframing tool, it came as a stand alone application, chrome extension or firefox extension, by default it doesn’t have any mobile widget so you need to install this pencil android util from Android Asset Studio.

Pencil Home page and Download page
Android Asset Studio
Download stencil/toolkits for Pencil

Other most useful wireframe sketcher tools

Read here a very good article about Android UI design, Navigation and Wireframe. 

Different implementation of progress dialog in android


Progress dialog Examples

As all novice suffers with progress dialog problem,
here is an example of all possible ways to implement
progress dialog in Android.

list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">	

    <ListView
 	android:id="@id/android:list"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
 		android:drawSelectorOnTop="true"/>

    <TextView
        android:id="@id/android:empty"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="@string/list_empty_msg"/>

</LinearLayout>

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/btn_launch_all_on_ui"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btn_on_ui_thread" />

    <Button
        android:id="@+id/btn_async_task_simple"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btn_asynctask" />

    <Button
        android:id="@+id/btn_async_task_simple_state"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btn_asynctask_statesaving" />

    <Button
        android:id="@+id/btn_async_task_simple_configured"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btn_asynctask_manifest" />

    <Button
        android:id="@+id/btn_async_task_complex"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btn_asynctask_complex" />

</LinearLayout>

row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/vw1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">    
    <TextView android:id="@+id/row_name"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"/>
</LinearLayout>

list_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/refresh_list"
        android:title="Refresh"/>
    <item
        android:id="@+id/clear_list"
        android:title="Clear"/>

</menu>

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, Progress Dialog Samples!</string>
    <string name="app_name">Progress Dialog Samples</string>
    <string name="list_empty_msg">No Data</string>
    <string name="btn_on_ui_thread">All On UI Thread</string>
    <string name="btn_asynctask">Using AsyncTask Simple</string>
    <string name="btn_asynctask_statesaving">Using AsyncTask With StateSaving</string>
    <string name="btn_asynctask_manifest">Using AsyncTask Manifest Configured</string>
    <string name="btn_asynctask_complex">Using AsyncTask Complex Logic</string>
</resources>

Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ecs.android.listview.sample"
    android:versionCode="1"
    android:versionName="1.0" >

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <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>
        <activity
            android:name=".AllOnUIThread"
            android:label="AllOnUIThread" />
        <activity
            android:name=".AsyncTaskSimple"
            android:label="AsyncTaskSimple" />
        <activity
            android:name=".AsyncTaskSimpleStateSaving"
            android:label="AsyncTaskSimpleStateSaving" />
        <activity
            android:name=".AsyncTaskSimpleConfigured"
            android:configChanges="keyboardHidden|orientation"
            android:label="AsyncTaskSimpleConfigured" />
        <activity
            android:name=".AsyncTaskComplex"
            android:label="AsyncTaskComplex" />
    </application>

</manifest>

MainActivity.java

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		Button btnAllOnUI = (Button) findViewById(R.id.btn_launch_all_on_ui);
		Button btnAsyncSimple = (Button) findViewById(R.id.btn_async_task_simple);
		Button btnAsyncSimpleStateSaving = (Button) findViewById(R.id.btn_async_task_simple_state);
		Button btnAsyncSimpleConfigured = (Button) findViewById(R.id.btn_async_task_simple_configured);
		Button btnAsyncComplex = (Button) findViewById(R.id.btn_async_task_complex);
		btnAllOnUI.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				startActivity(new Intent().setClass(v.getContext(),
						AllOnUIThread.class));
			}
		});

		btnAsyncSimple.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				startActivity(new Intent().setClass(v.getContext(),
						AsyncTaskSimple.class));
			}
		});
		
		btnAsyncSimpleStateSaving.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				startActivity(new Intent().setClass(v.getContext(),
						AsyncTaskSimpleStateSaving.class));
			}
		});
		
		
		
		btnAsyncSimpleConfigured.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				startActivity(new Intent().setClass(v.getContext(),
						AsyncTaskSimpleConfigured.class));
			}
		});		

		btnAsyncComplex.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				startActivity(new Intent().setClass(v.getContext(),
						AsyncTaskComplex.class));
			}
		});
	
	}
}

AbstractListActivity.java

/**
 * Abstract ListActivity class that implements some basic functionality for the samples.
 * It basically creates our list layout, and offers the following methods 
 * 
 * refreshListLongRunning (refreshes our list data)
 * refreshListView (refreshes our list control with the new data)
 * clearList (clears the list)
 * onCreateOptionsMenu (creates our menu - all activities in the sample have a refresh and clear list)
 * onCreateDialog (creates our dialog - LOADING_DIALOG)
 * 
 */
public abstract class AbstractListActivity extends ListActivity {
	
	protected static final int LOADING_DIALOG = 0;
	protected static final int LONG_RUNNING_TIME = 5000;

	private static final String TAG =  MainActivity.class.getSimpleName();
	
	protected List<Map<String, String>> listItems = new ArrayList<Map<String, String>>();
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list);
	}
	
	protected List<Map<String, String>> retrieveListLongRunning() {
		List<Map<String, String>> listItems;
		SystemClock.sleep(LONG_RUNNING_TIME);
		listItems = new ArrayList<Map<String, String>>();		
		for (int i=1 ; i<=10 ; i++) {
			Map<String, String> itemData = new HashMap<String, String>();
			itemData.put("name", "Item " + i);
			listItems.add(itemData);
		}
		Log.i(TAG, "done refreshList");
		return listItems;
	}
	
	protected void refreshListView() {
		SimpleAdapter notes = new SimpleAdapter(getApplicationContext(),
	            this.listItems,
	            R.layout.row,
	            new String[] { "name"},
	            new int[] { R.id.row_name,} );
	
		setListAdapter(notes);		
	}
	
	protected void clearList() {
		listItems = new ArrayList<Map<String, String>>();		
	}	
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
	    MenuInflater inflater = getMenuInflater();
	    inflater.inflate(R.menu.list_menu, menu);
	    return true; 
	}
	
	protected Dialog onCreateDialog(int id) {
		if(id == LOADING_DIALOG){
			ProgressDialog loadingDialog = new ProgressDialog(this);
			loadingDialog.setMessage("Loading records ...");
			loadingDialog.setIndeterminate(true);
			loadingDialog.setCancelable(true);
			return loadingDialog;
		} 
		return super.onCreateDialog(id);
	}	
}

AllOnUIThread.java

/**
 * This activity attempts to show the dialog on the main UI thread.
 * It shows the dialog before the list (and its data) is refreshed, and dismisses it afterwards.
 * As you'll notice, the progress dialog never shows, as all of this is done on the UI thread.  
 *
 */
public class AllOnUIThread extends AbstractListActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
    	showDialog(LOADING_DIALOG);
    	this.listItems = retrieveListLongRunning();
    	refreshListView();
    	dismissDialog(LOADING_DIALOG);
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
	    switch (item.getItemId()) {
		    case R.id.refresh_list:
		    	showDialog(LOADING_DIALOG);
		    	this.listItems = retrieveListLongRunning();
		    	refreshListView();
		    	dismissDialog(LOADING_DIALOG);
		        return true;	
		    case R.id.clear_list:
		    	clearList();
		    	refreshListView();
		        return true;		        
		    default:
		        return super.onOptionsItemSelected(item);
	    }
	}	
}

AsyncTaskComplex.java

public class AsyncTaskComplex extends AbstractListActivity {

	private static final String TAG =  AsyncTaskComplex.class.getSimpleName(); 

    private ListRefresher mTask; 
    private boolean mShownDialog; 
    
    /**
     * After a screen orientation change, we associate the current ( = newly created) activity
     * with the restored asyncTask.
     * On a clean activity startup, we create a new task and associate the current activity. 
     */
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Object retained = getLastNonConfigurationInstance(); 
        if ( retained instanceof ListRefresher ) { 
                Log.i(TAG, "Reclaiming previous background task."); 
                mTask = (ListRefresher) retained; 
                mTask.setActivity(this); 
        } else { 
                Log.i(TAG, "Creating new background task."); 
                mTask = new ListRefresher(this); 
                mTask.execute(); 
        }         
    }
	
	/**
	 * After a screen orientation change, this method is invoked.
	 * As we're going to state save the task, we can no longer associate 
	 * it with the Activity that is going to be destroyed here.
	 */
    @Override 
    public Object onRetainNonConfigurationInstance() { 
            mTask.setActivity(null); 
            return mTask; 
    } 	
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
	}

	/**
	 * When the aSyncTask has notified the activity that it has completed,
	 * we can refresh the list control, and attempt to dismiss the dialog.
	 * We'll only dismiss the dialog 
	 */
    private void onTaskCompleted() { 
        Log.i(TAG, "Activity " + this + " has been notified the task is complete."); 
        this.listItems = mTask.getItems();
        refreshListView();
        //Check added because dismissDialog throws an exception if the current 
        //activity hasn't shown it. This Happens if task finishes early enough 
        //before an orientation change that the dialog is already gone when 
        //the previous activity bundles up the dialogs to reshow. 
        if ( mShownDialog ) { 
                dismissDialog(LOADING_DIALOG); 
                Toast.makeText(this, "Finished..", Toast.LENGTH_LONG).show(); 
        } 
    } 	
	
    /**
     * Our complex async task that holds a reference to the Activity that started it,
     * and a boolean to determine if the task completed.
     */
	private class ListRefresher extends AsyncTask<Uri, Void, Void> {

		 private AsyncTaskComplex activity; 
         private boolean completed; 
         
         private List<Map<String, String>> items = new ArrayList<Map<String, String>>();
        
         private ListRefresher(AsyncTaskComplex activity) { 
                 this.activity = activity; 
         } 
         
         public List<Map<String, String>> getItems() {
			return items;
		}
         
        /**
         * Showing the dialog on the UI thread.
         */
		@Override
		protected void onPreExecute() {
			activity.showDialog(LOADING_DIALOG);
		}
		
        /**
         * Performing the heavy lifting in the background thread thread.
         */
		@Override
		protected Void doInBackground(Uri... params) {
			items = retrieveListLongRunning();		
			return null;
		}

		/**
		 * When the task is completed, notifiy the Activity.
		 */
        @Override 
        protected void onPostExecute(Void unused) { 
                completed = true; 
                notifyActivityTaskCompleted(); 
        } 
        
        private void setActivity(AsyncTaskComplex activity) { 
                this.activity = activity; 
                if ( completed ) { 
                	notifyActivityTaskCompleted(); 
                } 
        } 
        
        /**
         * Helper method to notify the activity that this task was completed.
         */
        private void notifyActivityTaskCompleted() { 
                if ( null != activity ) { 
                	activity.onTaskCompleted(); 
                } 
        }         
	}
	
	/**
	 * Here, we're maintaining the mShownDialog flag in the activity so that it knows that the 
	 * progress dialog has been shown. The flag is required when dismissing the dialog, as the only 
	 * activity that is allowed to dismiss the dialog is the activity that has also created it.
	 */
    @Override 
    protected void onPrepareDialog(int id, Dialog dialog) { 
            super.onPrepareDialog(id, dialog); 
            if ( id == LOADING_DIALOG ) { 
                    mShownDialog = true; 
            } 
    } 	

    /**
     * Here, we're executing the async task by passing on our activity.
     * Inside the async task, a reference to this activity is kept for proper dialog progress handling.
     */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
	    switch (item.getItemId()) {
		    case R.id.refresh_list:
		    	mTask = new ListRefresher(this); 
		    	mTask.execute();
		        return true;	
		    case R.id.clear_list:
		    	clearList();
		    	refreshListView();
		    	return true;
		    default:
		        return super.onOptionsItemSelected(item);
	    }
	    
	}
}

AsyncTaskSimple.java

/**
 * Instead of doing everything on the UI thread, this activity will use an AsyncTask.
 * We'll still show the dialog, refresh the list, and dismiss the dialog, but this time these
 * actions will be done in different threads.
 * On the start of the task, we'll show the dialog on the UI thread.
 * When executing the task (in the background thread), we'll refresh our list.
 * When the task has finished, we'll dismiss the dialog (back in the UI thread).
 * 
 * As opposed to the AllOnUIThread activity, this time, the progress dialog will be shown properly.
 * However, this example causes a problem during a screen orientation change.
 * 
 */
public class AsyncTaskSimple extends AbstractListActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		new ListRefresher().execute(); 
	}
	
	@Override
	protected void onDestroy() {
		removeDialog(LOADING_DIALOG);
		super.onDestroy();
	}
	
    @Override 
    public Object onRetainNonConfigurationInstance() {
    	return super.onRetainNonConfigurationInstance();
    } 	
    
    
	
	/**
	 * Our menu trigger will launch the asynctask, allowing a background thread to be spawned.
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
	    switch (item.getItemId()) {
		    case R.id.refresh_list:
		    	new ListRefresher().execute(); 
		        return true;	
		    case R.id.clear_list:
		    	clearList();
		    	refreshListView();
		        return true;		        
		    default:
		        return super.onOptionsItemSelected(item);
	    }
	    
	}
	
	/**
	 * Our background task split up into 3 sections
	 * 
	 * onPreExecute - showing the dialog in the UI thread (just before doInBackground is called).
	 * doInBackground - launches a seperate thread to refresh the list data
	 * onPostExecute - dismisses the dialog and refreshes the list control.
	 * 
	 */
	private class ListRefresher extends AsyncTask<Uri, Void, Void> {

		/**
		 * This is executed in the UI thread. The only place where we can show the dialog.
		 */
		@Override
		protected void onPreExecute() {
			showDialog(LOADING_DIALOG);
		}
		
		/**
		 * This is executed in the background thread. 
		 */
		@Override
		protected Void doInBackground(Uri... params) {
			listItems = retrieveListLongRunning();
			return null;
		}

		/**
		 * This is executed in the UI thread. The only place where we can show the dialog.
		 */
	   @Override 
       protected void onPostExecute(Void unused) { 
    	   dismissDialog(LOADING_DIALOG);
    	   refreshListView();
       } 
	}
	
}

AsyncTaskSimpleConfigured.java

/**
 * For this Activity, we're just extending the basic asynctask activity.
 * 
 * By having a seperate Activity, we can change the manifest 
 * for this activity with the following activity property : 
 * 
 * android:configChanges="keyboardHidden|orientation"
 * 
 */
public class AsyncTaskSimpleConfigured extends AsyncTaskSimple {

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
	  super.onConfigurationChanged(newConfig);
	  setContentView(R.layout.list);
	}

}

AsyncTaskSimpleStateSaving.java

/**
 * Instead of doing everything on the UI thread, this activity will use an AsyncTask.
 * We'll still show the dialog, refresh the list, and dismiss the dialog, but this time these
 * actions will be done in different threads.
 * On the start of the task, we'll show the dialog on the UI thread.
 * When executing the task (in the background thread), we'll refresh our list.
 * When the task has finished, we'll dismiss the dialog (back in the UI thread).
 * 
 * As opposed to the AllOnUIThread activity, this time, the progress dialog will be shown properly.
 * However, problems arise when changing the screen orientation.
 * 
 */
public class AsyncTaskSimpleStateSaving extends AbstractListActivity {

	/**
	 * Intelligent onCreate that takes into account the last known configuration instance.
	 * When an activity is destroyed (as a result of a screen rotate), the onRetainNonConfigurationInstance
	 * method is called, where we can return any state that we want to restore here.
	 */
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Object retained = getLastNonConfigurationInstance(); 
		if (retained==null) {
			new ListRefresher().execute();
		} else {
			this.listItems = (List<Map<String, String>>) retained; 
			refreshListView();
		}
	}
	
	/**
	 * Cache the already fetched listItems. 
	 * This will be picked up in the onCreate method.
	 */
    @Override 
    public Object onRetainNonConfigurationInstance() {
    	return listItems;
    } 	
	
	/**
	 * Our menu trigger will launch the asynctask, allowing a background thread to be spawned.
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
	    switch (item.getItemId()) {
		    case R.id.refresh_list:
		    	new ListRefresher().execute(); 
		        return true;	
		    case R.id.clear_list:
		    	clearList();
		    	refreshListView();
		        return true;		        
		    default:
		        return super.onOptionsItemSelected(item);
	    }
	    
	}
	
	/**
	 * Our background task split up into 3 sections
	 * 
	 * onPreExecute - showing the dialog in the UI thread (just before doInBackground is called).
	 * doInBackground - launches a seperate thread to refresh the list data
	 * onPostExecute - dismisses the dialog and refreshes the list control.
	 * 
	 */
	private class ListRefresher extends AsyncTask<Uri, Void, Void> {

		/**
		 * This is executed in the UI thread. The only place where we can show the dialog.
		 */
		@Override
		protected void onPreExecute() {
			showDialog(LOADING_DIALOG);
		}
		
		/**
		 * This is executed in the background thread. 
		 */
		@Override
		protected Void doInBackground(Uri... params) {
			listItems=retrieveListLongRunning();
			return null;
		}

		/**
		 * This is executed in the UI thread. The only place where we can show the dialog.
		 */
	   @Override 
       protected void onPostExecute(Void unused) { 
    	   dismissDialog(LOADING_DIALOG);
    	   refreshListView();
       } 
	}
	
}

We did it. Now run each options and see 
the diffrence. 

I got this similar example from GIT, I forgot the guy…. although I edited and fixed some bugs in his code, but also all credit goes to that guy.

How to decode .apk file or .class file

July 27, 2012 4 comments

Procedure to decoding .apk files 

Step 1:

Make a new folder (in this eg or case NEW_FOLDER) and put .apk file (which you want to decode, lets say MyDemoApplication.apk).
Now rename this MyDemoApplication.apk file with extension .zip (eg:rename from MyDemoApplication.apk to MyDemoApplication.apk.zip) and save it.
Now you are able to see drawable but not xml and classes.dex but not .java files etc…so cont…

Step 2:

Now extract this zip apk file in the same folder(We are using NEW_FOLDER).

Note: Although .apk file can be extracted, so you can directly extract without renaming file.

Now download dex2jar from this link http://code.google.com/p/dex2jar/ and extract it to the same folder (We are using NEW_FOLDER)
Now open command prompt and reach to that folder (in this case NEW FOLDER)
….after reaching write `./d2j-dex2jar.sh classes.dex` and press enter, as

[pankaj@localhost AIR]$ ./d2j-dex2jar.sh classes.dex

Note: I am using Linux, and I guess dex2jar provides executables for the same.

After successful excutaion of above command, you will get classes-dex2jar.jar file in the same folder.

Now download java decompiler from http://java.decompiler.free.fr/?q=jdgui, and now double click on jd-gui and click on open file then open classes-dex2jar.jar file from that folder

Its magic… you are able to see java codes. Save all these class file (click on file then click “file -> save all sources” in jd-gui)..by src name

At this stage you got source… but xml files are still unreadable…

so cont…

Step 3:

Now open another new folder (We will use NEW_FOLDER_XML_DECODES) and put these files

1. put .apk file which you want to decode

2. download apktool v1.x AND apktool install window using google and put in the same folder (We are using NEW_FOLDER_XML_DECODES)

3. Copy MyDemoApplication.apk file in the same folder (We are using NEW_FOLDER_XML_DECODES)

4. Open a command window

5. Navigate to the root directory of APKtool and type the following command:
      `apktool if MyDemoApplication.apk`

6. `apktool d MyDemoApplication.apk`  

Now you get a file folder in that folder and now you can easily read xml files also.

Step 4: 

It’s not any step just copy contents of both folder(in this case both new folder)to the single one

and now enjoy with source code…

 STEP 3 in details 

For additional info, see: http://code.google.com/p/android-apktool/

Step a: This step must be completed every time you are dealing with APKTool:

[pankaj@localhost ~]$ apktool if /home/pankaj/Download/MyDemoApplication.apk 
I: Framework installed to: /home/pankaj/apktool/framework/1.apk

Step b: This step does the actual decompiling of the APK

[pankaj@localhost ~]$ apktool d /home/pankaj/Download/MyDemoApplication.apk /home/pankaj/Download/DecodedCodeByAPKtool
I: Baksmaling...
I: Loading resource table...
I: Loaded.
I: Loading resource table from file: /home/pankaj/apktool/framework/1.apk
I: Loaded.
I: Decoding file-resources...
I: Decoding values*/* XMLs...
I: Done.
I: Copying assets and libs...
[pankaj@localhost ~]$ 

Other Ref : http://codexplo.wordpress.com/2012/03/15/how-to-decompile-android-apk-to-java-source-code/

I got one more interesting solution is here : 
AndroidSuite 2012

Record video : Android

July 24, 2012 2 comments

Record video demo

This is demo application to record video,
spliting with 4 files (each of 30 secs length).

When I was doing RnD with record video from 
background, I did this sample application, 
although this application doesn't record
video from background, but this splits videos
so this can help many people. 

Lets start……..

manifest code


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.exercise.AndroidVideoCapture"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="10" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
	<uses-permission android:name="android.permission.CAMERA"/>
	<uses-permission android:name="android.permission.RECORD_AUDIO"/>
	<uses-permission android:name="android.permission.RECORD_VIDEO"/>
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".AndroidVideoCapture"
                  android:label="@string/app_name"
                  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
                  android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

<FrameLayout
	android:id="@+id/videoview" 
	android:layout_width="720px"
	android:layout_height="480px"/>
<Button
	android:id="@+id/mybutton" 
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="REC"
	android:textSize="12dp"/>

</LinearLayout>

AndroidVideoCapture.java

package com.exercise.AndroidVideoCapture;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.content.Context;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;

public class AndroidVideoCapture extends Activity {

	private Camera myCamera;
	private MyCameraSurfaceView myCameraSurfaceView;
	private MediaRecorder mediaRecorder;
	private static int fileCounter = 0;

	Button myButton;
	SurfaceHolder surfaceHolder;
	boolean recording;
	Timer timer = new Timer();
	Handler handler = new Handler();

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		recording = false;

		setContentView(R.layout.main);

		// Get Camera for preview
		myCamera = getCameraInstance();
		if (myCamera == null) {
			Toast.makeText(AndroidVideoCapture.this, "Fail to get Camera",
					Toast.LENGTH_LONG).show();
		}

		myCameraSurfaceView = new MyCameraSurfaceView(this, myCamera);
		FrameLayout myCameraPreview = (FrameLayout) findViewById(R.id.videoview);
		myCameraPreview.addView(myCameraSurfaceView);

		myButton = (Button) findViewById(R.id.mybutton);
		myButton.setOnClickListener(myButtonOnClickListener);
		//timer.schedule(myTask, 0 /*starts now*/, 10 * 1000 /* every 10 seconds */);
			// whenever you need to cancel it:
			//timer.cancel();
	}

	TimerTask myTask = new TimerTask() {
		public void run() {
			handler.post(new Runnable() {
				
				public void run() {
					recordTasker();
				}
			});
		}
	};
		
	Button.OnClickListener myButtonOnClickListener = new Button.OnClickListener() {

		public void onClick(View v) {
			/*if("STOP".equalsIgnoreCase(myButton.getText().toString())) {
				timer.cancel();
			} else {
				timer.schedule(myTask, 0 starts now, 300 * 1000  every 10 seconds );
			}*/
			
			// TODO Auto-generated method stub
			if (recording) {
				// stop recording and release camera
				mediaRecorder.stop(); // stop the recording
				releaseMediaRecorder(); // release the MediaRecorder object
				
				myButton.setText("REC");
				recording = false;
				// Exit after saved
				//finish();
			} else {

				// Release Camera before MediaRecorder start
				releaseCamera();

				if (!prepareMediaRecorder()) {
					Toast.makeText(AndroidVideoCapture.this,
							"Fail in prepareMediaRecorder()!\n - Ended -",
							Toast.LENGTH_LONG).show();
					finish();
				}

				mediaRecorder.start();
				recording = true;
				fileCounter = fileCounter + 1;
				myButton.setText("STOP (" + fileCounter + ")");
			}
		}
	};

	private Camera getCameraInstance() {
		// TODO Auto-generated method stub
		Camera c = null;
		try {
			c = Camera.open(); // attempt to get a Camera instance
		} catch (Exception e) {
			// Camera is not available (in use or does not exist)
		}
		return c; // returns null if camera is unavailable
	}

	private boolean prepareMediaRecorder() {
		myCamera = getCameraInstance();
		mediaRecorder = new MediaRecorder();

		myCamera.unlock();
		mediaRecorder.setCamera(myCamera);

		mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
		mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

		mediaRecorder.setProfile(CamcorderProfile
				.get(CamcorderProfile.QUALITY_HIGH));

		 // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); //TODO
        
		mediaRecorder.setOutputFile("/sdcard/" + timeStamp + "myVideo.mp4");
		mediaRecorder.setMaxDuration(30000); // Set max duration 30 sec.
		//Commented because if we set maximum size it may occuere before maximum duration
		//and in our case, file must be of 30 seconds (size doesn't matter)
		//mediaRecorder.setMaxFileSize(5000000); // Set max file size 5M

		mediaRecorder.setPreviewDisplay(myCameraSurfaceView.getHolder().getSurface());
		mediaRecorder.setOnInfoListener(null);
		mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
			public void onInfo(MediaRecorder mr, int what, int extra) {

				if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
					Toast.makeText(AndroidVideoCapture.this, "Maximum duration reached", Toast.LENGTH_LONG).show();
					handler.post(new Runnable() {
						
						public void run() {
							recordTasker();
						}
					});
				}
			}

		}); 
			
		try {
			mediaRecorder.prepare();
		} catch (IllegalStateException e) {
			e.printStackTrace();
			releaseMediaRecorder();
			return false;
		} catch (IOException e) {
			e.printStackTrace();
			releaseMediaRecorder();
			return false;
		}
		return true;

	}

	@Override
	protected void onPause() {
		super.onPause();
		timer.cancel();
		releaseMediaRecorder(); // if you are using MediaRecorder, release it
								// first
		releaseCamera(); // release the camera immediately on pause event
	}

	private void releaseMediaRecorder() {
		if (mediaRecorder != null) {
			mediaRecorder.reset(); // clear recorder configuration
			mediaRecorder.release(); // release the recorder object
			mediaRecorder = null;
			myCamera.lock(); // lock camera for later use
		}
	}

	private void releaseCamera() {
		if (myCamera != null) {
			myCamera.release(); // release the camera for other applications
			myCamera = null;
		}
	}

	public class MyCameraSurfaceView extends SurfaceView implements
			SurfaceHolder.Callback {

		private SurfaceHolder mHolder;
		private Camera mCamera;

		public MyCameraSurfaceView(Context context, Camera camera) {
			super(context);
			mCamera = camera;

			// Install a SurfaceHolder.Callback so we get notified when the
			// underlying surface is created and destroyed.
			mHolder = getHolder();
			mHolder.addCallback(this);
			// deprecated setting, but required on Android versions prior to 3.0
			mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		}

		public void surfaceChanged(SurfaceHolder holder, int format,
				int weight, int height) {
			// If your preview can change or rotate, take care of those events
			// here.
			// Make sure to stop the preview before resizing or reformatting it.

			if (mHolder.getSurface() == null) {
				// preview surface does not exist
				return;
			}

			// stop preview before making changes
			try {
				mCamera.stopPreview();
			} catch (Exception e) {
				// ignore: tried to stop a non-existent preview
			}

			// make any resize, rotate or reformatting changes here

			// start preview with new settings
			try {
				mCamera.setPreviewDisplay(mHolder);
				mCamera.startPreview();

			} catch (Exception e) {
			}
		}

		public void surfaceCreated(SurfaceHolder holder) {
			// TODO Auto-generated method stub
			// The Surface has been created, now tell the camera where to draw
			// the preview.
			try {
				mCamera.setPreviewDisplay(holder);
				mCamera.startPreview();
			} catch (IOException e) {
			}
		}

		public void surfaceDestroyed(SurfaceHolder holder) {
			// TODO Auto-generated method stub

		}
	}
	
	private void showToast() {
		Toast.makeText(AndroidVideoCapture.this, "From UI thread ", Toast.LENGTH_SHORT).show();
	}
	
	private void recordTasker() {
		//By default stop recorder 
		if(fileCounter >= 4) {
			mediaRecorder.stop(); // stop the recording
			releaseMediaRecorder(); // release the MediaRecorder object			
			Toast.makeText(AndroidVideoCapture.this, "Record END ", Toast.LENGTH_SHORT).show();
			myButton.setText("REC");
			fileCounter = 0;
			recording = false;
		} else {
			//Continue with recording
			if (recording) {
				// stop recording and release camera
				mediaRecorder.stop(); // stop the recording
				releaseMediaRecorder(); // release the MediaRecorder object				
				//Toast.makeText(AndroidVideoCapture.this, "Record END ", Toast.LENGTH_SHORT).show();
				myButton.setText("REC");
				recording = false;
				// Exit after saved
				//finish();
			} 
			// Release Camera before MediaRecorder start
			releaseCamera();

			if (!prepareMediaRecorder()) {
				Toast.makeText(AndroidVideoCapture.this,
						"Fail in prepareMediaRecorder()!\n - Ended -",
						Toast.LENGTH_LONG).show();
				finish();
			}

			mediaRecorder.start();
			recording = true;
			//Toast.makeText(AndroidVideoCapture.this, "Record START", Toast.LENGTH_SHORT).show();
			fileCounter = fileCounter + 1;
			myButton.setText("STOP (" + fileCounter + ")");
		}
		
	}
}

Enjoy……

How to calculate average difference between the dates of all the rows with a SQL query.

July 6, 2012 1 comment

Basically solution of this question can be more than one.
In my case, first time I was calculating diffrence between 1st and 2nd date, 2nd and 3rd date …. (so on).
At the same time I was adding results, and at last dividing by total row. This was giving currect result
but this made my query slow.


So I followed another approach, and made my query faster and I don’t think you need to calculate all the differences.

Here we go

Lets say my table structure is

order_details(
od_id [int(11)] ,
od_date [datetime],
cust_phone [varchar(15)]
)

I want to know how often (on average) a row was inserted.

Logic :-

Its clear that I only need to sum up the differences between adjacent rows (adjacent based on the timestamp) and divide the result by the number of the summands.

The formula

((T1-T0) + (T2-T1) + … + (TN-TN-1)) / N

can obviously be simplified to merely

(TN-T0) / N

It means I need difference between max and min date of od_date and divide by total count.
So, the query would be something like this:

SELECT TIMESTAMPDIFF(SECOND, MIN(od_date), MAX(od_date)) / (COUNT(*) - 1)
FROM order_details

Make sure the number of rows is more than 1, or you’ll get the Division By Zero error. Still, if you like,
you can prevent the error with a simple trick:

SELECT
  IFNULL(TIMESTAMPDIFF(SECOND, MIN(od_date), MAX(od_date)) / NULLIF(COUNT(*) - 1, 0), 0)
FROM order_details

Now you can safely run the query against a table with a single row.
Result is in Seconds, you can change it as hour, minute, day as your requirement.