package com.ywwxhz.update; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.support.v4.app.NotificationCompat; import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.util.Log; import android.util.TypedValue; import android.widget.TextView; import android.widget.Toast; import com.google.gson.Gson; import com.loopj.android.http.FileAsyncHttpResponseHandler; import com.loopj.android.http.TextHttpResponseHandler; import com.ywwxhz.lib.kits.FileKit; import com.ywwxhz.lib.kits.NetKit; import com.ywwxhz.lib.kits.PrefKit; import com.ywwxhz.update.listener.OnUpdateListener; import com.ywwxhz.update.pojo.UpdateInfo; import org.apache.http.Header; import java.io.File; public class UpdateHelper { private File apkFile; private Options options; private String url = ""; private Context mContext; private UpdateInfo updateInfo; private boolean running = false; private OnUpdateListener updateListener; private NotificationManager notificationManager; private NotificationCompat.Builder ntfBuilder; private static UpdateHelper instance; private static UpdateReceiver receiver; private static String installPackageAction; private static final int DOWNLOAD_NOTIFICATION_ID = 0x3; private static final String ACTION_PREFIX = ".update.InstallPackage"; private static void init(Context context) { if (receiver == null) { receiver = new UpdateReceiver(); IntentFilter filter = new IntentFilter(); installPackageAction = context.getPackageName() + ACTION_PREFIX; filter.addAction(installPackageAction); context.registerReceiver(receiver, filter); } } private static void destroy(Context context) { if (receiver != null) { context.unregisterReceiver(receiver); } receiver = null; } private UpdateHelper(Context context, String url, Options options) { this.mContext = context; this.url = url; notificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); init(context.getApplicationContext()); setOptions(options); } private UpdateHelper(Context context, String url) { this(context, url, null); } public void setOptions(Options options) { this.options = options == null ? new Options() : options; if (this.options.savePath == null) { this.options.savePath = FileKit.isExternalStorageAvalible() ? new File(mContext.getExternalCacheDir(), "update") : new File(mContext.getCacheDir(), "update"); } } public void check() { check(null); } public void check(OnUpdateListener listener) { if (listener != null) { this.updateListener = listener; } if (mContext == null) { Log.e("NullPointerException", "The context must not be null."); return; } if (!running) { running = true; NetKit.getAsyncClient().get(url, versionCheckHandler); } } private TextHttpResponseHandler versionCheckHandler = new TextHttpResponseHandler() { @Override public void onStart() { if (updateListener != null) { updateListener.onStartCheck(); } } @Override public void onSuccess(int statusCode, Header[] headers, String responseString) { System.out.println(responseString); updateInfo = new Gson().fromJson(responseString, UpdateInfo.class); if (mContext != null && updateInfo != null) { int ignoreVersionCode = PrefKit.getInt(mContext, "ignoreVersionCode", 0); if (updateInfo.getVersionCode() > getPackageInfo().versionCode && (options.showIgnoreVersion || ignoreVersionCode != updateInfo.getVersionCode())) { showUpdateUI(); } else { running = false; if (options.hintVersion) { Toast.makeText(mContext, "当前已是最新版", Toast.LENGTH_LONG).show(); } } } else { running = false; if (options.hintVersion && mContext != null) { Toast.makeText(mContext, "当前已是最新版", Toast.LENGTH_LONG).show(); } } if (UpdateHelper.this.updateListener != null) { UpdateHelper.this.updateListener.onFinishCheck(updateInfo); } } @Override public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { running = false; if (options.hintVersion && mContext != null) { Toast.makeText(mContext, "当前已是最新版", Toast.LENGTH_LONG).show(); } } }; private void showNetDialog() { DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (which == AlertDialog.BUTTON_POSITIVE) { startDownload(); } dialog.dismiss(); } }; new AlertDialog.Builder(mContext) .setTitle("下载提示") .setMessage("您在目前的网络环境下继续下载将可能会消耗手机流量,请确认是否继续下载?") .setNegativeButton("取消下载", listener) .setPositiveButton("继续下载", listener) .create().show(); } /** * 弹出提示更新窗口 */ private void showUpdateUI() { DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case AlertDialog.BUTTON_POSITIVE: int type = NetKit.getNetType(mContext); if (type != 1) { showNetDialog(); } else { startDownload(); } break; case AlertDialog.BUTTON_NEGATIVE: running = false; break; case AlertDialog.BUTTON_NEUTRAL: running = false; PrefKit.writeInt(mContext, "ignoreVersionCode", updateInfo.getVersionCode()); break; } dialog.dismiss(); } }; AlertDialog dialog = new AlertDialog.Builder(mContext) .setTitle(updateInfo.getUpdateTips()) .setMessage(updateInfo.buildUpdateMessage(mContext)) .setNegativeButton("下次再说", listener) .setNeutralButton("忽略该版本", listener) .setPositiveButton("立即更新", listener) .show(); TextView textView = (TextView) dialog.findViewById(android.R.id.message); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15); } public void cleanDownload() { FileKit.deleteDir(options.savePath); } private void startDownload() { Toast.makeText(mContext, "开始下载更新", Toast.LENGTH_SHORT).show(); String apkName = updateInfo.getAppName() + "-" + updateInfo.getVersionCode() + ".apk"; if (!options.savePath.exists()) options.savePath.mkdirs(); apkFile = new File(options.savePath, apkName); if (apkFile.exists() && apkFile.length() == updateInfo.getSize()) { downloadComplate(); if (UpdateHelper.this.updateListener != null) { UpdateHelper.this.updateListener.onFinshDownload(); } return; } FileAsyncHttpResponseHandler downLoadFileHandler = new FileAsyncHttpResponseHandler(apkFile) { int oldProgress; @Override public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) { NotificationCompat.Builder ntfBuilder = new NotificationCompat.Builder(mContext) .setSmallIcon(mContext.getApplicationInfo().icon) .setContentTitle("更新失败") .setContentText("更新下载失败") .setTicker("更新下载失败"); notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, ntfBuilder.build()); running = false; } @Override public void onSuccess(int statusCode, Header[] headers, File file) { new Thread(new Runnable() { @Override public void run() { if (FileKit.valifyFileMd5(apkFile, updateInfo.getMd5())) { downloadComplate(); } else { apkFile.delete(); NotificationCompat.Builder ntfBuilder = new NotificationCompat.Builder(mContext) .setSmallIcon(mContext.getApplicationInfo().icon) .setContentTitle("更新失败") .setContentText("文件校验失败") .setTicker("文件校验失败"); notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, ntfBuilder.build()); running = false; } } }).start(); } @Override public void onProgress(int bytesWritten, int totalSize) { int progress = (int) ((bytesWritten / (float) totalSize) * 100); if (progress != oldProgress) { showDownloadNotificationUI(progress, bytesWritten, totalSize); if (UpdateHelper.this.updateListener != null) { UpdateHelper.this.updateListener.onDownloading(progress); } oldProgress = progress; } } }; ntfBuilder = new NotificationCompat.Builder(mContext) .setSmallIcon(mContext.getApplicationInfo().icon) .setTicker("开始下载...") .setOngoing(true) .setContentTitle("下载更新中...") .setProgress(100, 0, true); notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, ntfBuilder.build()); NetKit.getAsyncClient().get(updateInfo.getApkUrl(), downLoadFileHandler); } /** * 通知栏弹出下载提示进度 * * @param progress */ private void showDownloadNotificationUI(int progress, int bytesWritten, int totalSize) { if (mContext != null) { if (progress == 100) { ntfBuilder.setContentText("文件校验中"); ntfBuilder.setProgress(100, 0, true); } else { ntfBuilder.setContentText(FileKit.formatFileSize(mContext, bytesWritten) + " / " + FileKit.formatFileSize(mContext, totalSize)); ntfBuilder.setProgress(100, progress, false); } notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, ntfBuilder.build()); } } /** * 获取当前app版本 * * @return PackageInfo * @throws PackageManager.NameNotFoundException */ private PackageInfo getPackageInfo() { PackageInfo pinfo = null; if (mContext != null) { try { pinfo = mContext.getPackageManager().getPackageInfo( mContext.getPackageName(), 0); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } return pinfo; } private void installApk(Uri data) { if (mContext != null) { Intent i = new Intent(Intent.ACTION_VIEW); i.setDataAndType(data, "application/vnd.android.package-archive"); i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(i); notificationManager.cancel(DOWNLOAD_NOTIFICATION_ID); } else { Log.e("NullPointerException", "The context must not be null."); } } private void downloadComplate() { Toast.makeText(mContext, "更新下载完毕", Toast.LENGTH_SHORT).show(); if (options.autoInstall) { installApk(Uri.fromFile(apkFile)); } else { NotificationCompat.Builder ntfBuilder = new NotificationCompat.Builder(mContext) .setSmallIcon(mContext.getApplicationInfo().icon) .setContentTitle("下载完成") .setContentText("点击安装").setTicker("下载完成"); Intent intent = new Intent(mContext, UpdateReceiver.class); intent.setAction(installPackageAction); intent.setData(Uri.fromFile(apkFile)); PendingIntent pendingIntent = PendingIntent.getBroadcast( mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); ntfBuilder.setContentIntent(pendingIntent); notificationManager.notify(DOWNLOAD_NOTIFICATION_ID, ntfBuilder.build()); } running = false; } public static UpdateHelper build(Context context, String url, Options options) { if (instance == null) { instance = new UpdateHelper(context, url, options); } else { instance.setOptions(options); instance.update(context, url, options); } return instance; } private void update(Context context, String url, Options options) { if (context == null) { throw new IllegalArgumentException("context can't be null"); } if (TextUtils.isEmpty(url)) { throw new IllegalArgumentException("url can't be null or empty"); } mContext = context; this.url = url; setOptions(options); } public static UpdateHelper build(Context context, String url) { if (instance == null) { instance = new UpdateHelper(context, url); } else { instance.update(context, url, null); } return instance; } public static class UpdateReceiver extends BroadcastReceiver { public UpdateReceiver() { super(); } @Override public void onReceive(Context context, Intent intent) { UpdateHelper.destroy(context.getApplicationContext()); Log.v("UpdateReceiver", intent.getData().toString()); Intent i = new Intent(Intent.ACTION_VIEW); i.setDataAndType(intent.getData(), "application/vnd.android.package-archive"); i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(i); NotificationManager notificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(DOWNLOAD_NOTIFICATION_ID); } } public static class Options { private File savePath; private boolean autoInstall = false; private boolean hintVersion = false; private boolean showIgnoreVersion = false; public Options() { } public Options setAutoInstall(boolean autoInstall) { this.autoInstall = autoInstall; return this; } public Options setHintVersion(boolean hintVersion) { this.hintVersion = hintVersion; return this; } public Options setSavePath(File savePath) { this.savePath = savePath; return this; } public Options setShowIgnoreVersion(boolean showIgnoreVersion) { this.showIgnoreVersion = showIgnoreVersion; return this; } } }