package dk.thoerup.androidutils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.URL; import java.net.URLConnection; import android.app.AlertDialog; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.DialogInterface.OnClickListener; 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.os.AsyncTask; import android.os.Environment; import android.util.Log; public class CheckUpdates { final static long TIMESPAN_DAY = 24*60*60*1000; // one day final static String CHECKUPDATES = "CheckUpdates"; final static String LASTCHECK = "lastcheck"; int versionCode; String versionName; String packageName; String phone_model; String androidVersion; String title; Context context; public void checkForUpdates(Context context, String url, String title, String apkUrl) { checkForUpdates(context, url, title, apkUrl, false); } public void checkForUpdates(Context context, String url, String title, String apkUrl, boolean forced) { this.context = context; this.title = title; SharedPreferences prefs = context.getSharedPreferences(CHECKUPDATES, Context.MODE_PRIVATE); long lastCheck = prefs.getLong(LASTCHECK, 0); long now = System.currentTimeMillis(); //should i use android.os.SystemClock.elapsedRealtime() instead ? Log.i(CHECKUPDATES, "Now=" + now + ", lastCheck=" + lastCheck + ", lastCheck+timespan=" + (lastCheck + TIMESPAN_DAY) ); if ( now > (lastCheck+TIMESPAN_DAY) ){ PackageManager pm = context.getPackageManager(); try { PackageInfo pi; pi = pm.getPackageInfo(context.getPackageName(), 0); versionCode = pi.versionCode; versionName = pi.versionName; packageName = pi.packageName; phone_model = android.os.Build.MODEL; androidVersion = android.os.Build.VERSION.RELEASE; UpdateTask task = new UpdateTask(forced); task.execute(url, apkUrl); } catch (NameNotFoundException e) { Log.e(CHECKUPDATES, "NamingException", e); } } } class UpdateTask extends AsyncTask { boolean result = false; int newestVersion = 0; String apkUrl; boolean forced = false; public UpdateTask(boolean forced) { this.forced = forced; } @Override protected Void doInBackground(String... arg0) { String requestUrl = arg0[0] + "?version=" + encode(versionName) + "&phone=" + encode(phone_model) + "&android=" + encode(androidVersion); apkUrl = arg0[1]; try { URL url = new URL(requestUrl); URLConnection conn = url.openConnection(); conn.setConnectTimeout(2500); String resultStr = readIOStream( conn.getInputStream() ); resultStr = resultStr.trim(); newestVersion = Integer.parseInt(resultStr); result = true; } catch (Exception e) { Log.e(CHECKUPDATES, "Check for " + packageName + " failed!", e); } return null; } private String encode(String data) { try { return java.net.URLEncoder.encode(data, "UTF-8"); } catch (Exception e) { return data; //if encoding fails, return original and hope all goes well } } private String readIOStream(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte buf[] = new byte[1024]; int len; while ( (len=in.read(buf)) != -1) { baos.write(buf, 0, len); } return baos.toString(); } @Override protected void onPostExecute(Void r) { super.onPostExecute(r); Log.i(CHECKUPDATES, "result=" + result + ", newestVersion" + newestVersion + ", versionCode=" + versionCode); if (result == true) { long now = System.currentTimeMillis(); SharedPreferences prefs = context.getSharedPreferences(CHECKUPDATES, Context.MODE_PRIVATE); //when done write Editor edit = prefs.edit(); edit.putLong(LASTCHECK, now ); edit.commit(); if (newestVersion > versionCode) { String launchUrl = (apkUrl == null) ? "market://search?q=pname:" + packageName : apkUrl; if (forced == true) { forcedUpdate(launchUrl); } else { //showUpdateDialog(launchUrl); showNotification(launchUrl); } } } } private void forcedUpdate(String apkUrl) { Log.e(CHECKUPDATES, "Forced update started"); try { byte[] apkData = HttpUtil.getContent(apkUrl, 5000); File tempFile = new File( Environment.getExternalStorageDirectory() + "/" + HttpUtil.getLastPart(apkUrl) ); if (tempFile.exists()) { tempFile.delete(); } RandomAccessFile raf = new RandomAccessFile(tempFile,"rw"); raf.write(apkData); Log.e(CHECKUPDATES, "File:" + tempFile.getPath()); Intent i =new Intent(); i.setAction(Intent.ACTION_VIEW); i.setDataAndType( Uri.fromFile(tempFile), "application/vnd.android.package-archive"); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK ); try { context.startActivity(i); } catch (Exception e) { Log.e(CHECKUPDATES, "Activity launch failed", e); } context = null; } catch (Exception e) { Log.e(CHECKUPDATES, "forcedUpdate failed", e); } } private void showNotification(String launchUrl) { NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); //int icon = android.R.drawable.sym_action_email; //TODO: find a better icon int icon = R.drawable.searchicon; Notification notification = new Notification(icon, "Update available", System.currentTimeMillis() ); notification.flags |= Notification.FLAG_AUTO_CANCEL ; CharSequence contentTitle = title; CharSequence contentText = "New version available."; Intent notificationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(launchUrl) ); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK ); PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); nm.notify(1, notification); } private void showUpdateDialog(final String launchUrl) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(title); builder.setMessage("This application has an a newer version available. Update now?"); builder.setPositiveButton("Yes", new OnClickListener() { public void onClick(DialogInterface dialog, int which) { Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(launchUrl) ); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK ); try { context.startActivity(i); } catch (Exception e) { Log.e(CHECKUPDATES, "Activity launch failed", e); } context = null; } }); builder.setNegativeButton("No", new OnClickListener() { public void onClick(DialogInterface dialog, int which) { context = null; } }); try { builder.show(); } catch (Exception e) { Log.e(CHECKUPDATES, "Builder.show failed", e); } } } }