package com.integralblue.callerid.inject; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import android.app.AlertDialog; import android.app.Application; import android.app.Dialog; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.widget.Toast; import com.google.inject.Inject; import com.integralblue.callerid.R; public class VersionInformationHelper { public static final String PROMPT_FOR_NEW_VERSION_PREFERENCE = "promptForNewVersion"; public static final String LATEST_VERSION_PREFERENCE = "latestVersion"; @Inject PackageInfo packageInfo; @Inject Application application; @Inject SharedPreferences sharedPreferences; /** Should we prompt the user to upgrade? * Takes into consideration if there a later version available and if the user has expressed that he does not want to be prompted. * @return */ public boolean shouldPromptForNewVersion(){ if (isLaterVersionAvailable() && sharedPreferences.getBoolean(PROMPT_FOR_NEW_VERSION_PREFERENCE, true)) { return true; }else{ return false; } } /** Tell the user how to upgrade. * May open a browser, open a market app, or something else. */ public void showNewVersionInformation(){ final String signatureMD5 = getApplicationSignatureMD5(); if(application.getString(R.string.integralblue_signature_md5).equals(signatureMD5)){ //This is an "officially signed" apk. Send them to the Android market for an update. //Note that this apk might not have come from the Android market - but it could have. try{ application.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + application.getPackageName()))); return; }catch(ActivityNotFoundException e){ //ignore the exception, fall through to the fallback case } } //This apk doesn't use my "official" signature, or the Android market is not installed. //We don't know how they got the app, so //send them to the app home page and ask them to upgrade using whatever means //they'd like. application.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(application.getString(R.string.app_home_url)))); Toast.makeText(application, R.string.new_version_unknown_signature, Toast.LENGTH_LONG).show(); } /** Should we allow the user to be prompted if there's a new version available? * If the user expresses that they never want to be prompted, for example, call this method with "false" as the parameter * @param shouldPrompt */ public void setAllowPromptForNewVersion(boolean shouldPrompt){ Editor e = sharedPreferences.edit(); e.putBoolean(PROMPT_FOR_NEW_VERSION_PREFERENCE, shouldPrompt); final boolean commitRet = e.commit(); assert (commitRet); } /** Get the version code of the currently running application * @return */ public int getCurrentVersionCode(){ return packageInfo.versionCode; } /** Get the latest version code of the latest known version of the application. * May be -1 if no version information is available. * @return */ public int getLatestVersionCode(){ return sharedPreferences.getInt(LATEST_VERSION_PREFERENCE, -1); } /** Set the latest known version code. * Should only be called when we become aware of a new version code. * @param versionCode */ public void setLatestVersionCode(int versionCode){ if(versionCode > getLatestVersionCode()){ //the version info from the server is greater than what we believe the latest version is //save the version info in a preference for access elsewhere Editor editor = sharedPreferences.edit(); editor.putInt(LATEST_VERSION_PREFERENCE, versionCode); editor.commit(); } } /** Is there a later version of the application available than what is currently running? * @return */ public boolean isLaterVersionAvailable(){ return getCurrentVersionCode() < getLatestVersionCode(); } /** Get the MD5 of the signature of this application * @return */ public String getApplicationSignatureMD5(){ // The packageInfo member variable was not retrieved with the GET_SIGNATURES flag // So we need to get a different pacakgeInfo final PackageInfo packageInfo; try { packageInfo = application.getPackageManager().getPackageInfo(application.getPackageName(),PackageManager.GET_SIGNATURES); } catch (NameNotFoundException e) { throw new RuntimeException(e); } if(packageInfo.signatures!=null && packageInfo.signatures.length>0 && packageInfo.signatures[0]!=null){ return md5(packageInfo.signatures[0].toByteArray()); }else{ return null; } } private static String md5(byte[] s) { try { // Create MD5 Hash MessageDigest digest = java.security.MessageDigest .getInstance("MD5"); digest.update(s); byte messageDigest[] = digest.digest(); // Create Hex String StringBuffer hexString = new StringBuffer(); for (int i = 0; i < messageDigest.length; i++){ hexString.append(String.format("%02X", 0xFF & messageDigest[i])); } return hexString.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("How can the MD5 message digest algorithm not be available?!",e); } } public Dialog createNewVersionDialog(Context context){ return (new AlertDialog.Builder(context) .setTitle(R.string.new_version_dialog_title) .setPositiveButton(R.string.new_version_dialog_upgrade_button_text, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { showNewVersionInformation(); dialog.dismiss(); } }) .setNeutralButton(R.string.new_version_dialog_not_now_button_text, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }) .setNegativeButton(R.string.new_version_dialog_never_button_text, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { setAllowPromptForNewVersion(false); dialog.cancel(); } }).create()); } }