/** * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr) * This file is part of CSipSimple. * * CSipSimple is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * If you own a pjsip commercial license you can also redistribute it * and/or modify it under the terms of the GNU Lesser General Public License * as an android library. * * CSipSimple is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CSipSimple. If not, see <http://www.gnu.org/licenses/>. */ package com.csipsimple.utils; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.TextUtils; import com.csipsimple.R; import com.csipsimple.service.DeviceStateReceiver; import com.csipsimple.service.Downloader; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; public class NightlyUpdater { private static final String THIS_FILE = "NightlyUpdater"; public static final String LAST_NIGHTLY_CHECK = "nightly_check_date"; public static final String IGNORE_NIGHTLY_CHECK = "nightly_check_ignore"; private static final String DOWNLOADED_VERSION = "dl_version"; private Context context; private SharedPreferences prefs; private PackageInfo pinfo; private String channel; public NightlyUpdater(Context ctxt) { prefs = PreferenceManager.getDefaultSharedPreferences(ctxt); context = ctxt; pinfo = PreferencesProviderWrapper.getCurrentPackageInfos(context); channel = getChannelFolder(context); } private final static String BASE_UPDATE_VERSION = CustomDistribution.getNightlyUpdaterURL(); private final static String META_TYPE = "app_type"; private final static String META_CHANNEL = "app_channel"; private final static String NIGHTLY_TYPE = "nightly"; //private final static String RELEASE_TYPE = "release"; private static Bundle getApplicationMetaData(Context ctxt) throws NameNotFoundException { ApplicationInfo apInfo = ctxt.getPackageManager().getApplicationInfo(ctxt.getPackageName(), PackageManager.GET_META_DATA); return apInfo.metaData; } public static boolean isNightlyBuild(Context ctxt) { try { Bundle metaData = getApplicationMetaData(ctxt); if(metaData != null) { String appType = metaData.getString(META_TYPE); if(!TextUtils.isEmpty(appType)) { if (!TextUtils.isEmpty(BASE_UPDATE_VERSION)) { if (NIGHTLY_TYPE.equalsIgnoreCase(appType)) { return true; } } } } } catch (NameNotFoundException e) { Log.e(THIS_FILE, "Not able to get self app info", e); } return false; } public static String getChannelFolder(Context ctxt) { try { Bundle metaData = getApplicationMetaData(ctxt); if(metaData != null) { String appChannel = metaData.getString(META_CHANNEL); if(!TextUtils.isEmpty(appChannel)) { return appChannel; } } } catch (NameNotFoundException e) { Log.e(THIS_FILE, "Not able to get self app info", e); } return "trunk"; } private int getLastOnlineVersion() { try { URL url = new URL(BASE_UPDATE_VERSION + channel + "/CSipSimple-latest-" + channel + ".version"); Log.d(THIS_FILE, "Url : "+url); InputStream content = (InputStream) url.getContent(); if(content != null) { BufferedReader r = new BufferedReader(new InputStreamReader(content)); String versionString = r.readLine(); return Integer.parseInt(versionString); } } catch (MalformedURLException e) { Log.e(THIS_FILE, "Invalid nightly build url", e); } catch (IOException e) { Log.e(THIS_FILE, "Can't get nightly latest value", e); } catch (NumberFormatException e) { Log.e(THIS_FILE, "Invalid number format", e); } return 0; } public boolean ignoreCheckByUser() { return prefs.getBoolean(IGNORE_NIGHTLY_CHECK, false); } public long lastCheck() { return prefs.getLong(LAST_NIGHTLY_CHECK, (long) 0); } public UpdaterPopupLauncher getUpdaterPopup(boolean fallbackAlert) { UpdaterPopupLauncher popLauncher = null; Editor edt = prefs.edit(); int onlineVersion = getLastOnlineVersion(); // Reset ignore check value edt.putBoolean(IGNORE_NIGHTLY_CHECK, false); if(pinfo != null && pinfo.versionCode < onlineVersion) { popLauncher = new UpdaterPopupLauncher(context, onlineVersion); }else { // Set last check to now :) edt.putLong(LAST_NIGHTLY_CHECK, System.currentTimeMillis()); // And delete latest nightly from cache File cachedFile = getCachedFile(); if(cachedFile.exists()) { cachedFile.delete(); } if(fallbackAlert) { popLauncher = new UpdaterPopupLauncher(context, 0); } } edt.commit(); return popLauncher; } private File getCachedFile() { return new File(context.getCacheDir(), "CSipSimple-latest-trunk.apk"); } public class UpdaterPopupLauncher implements Runnable { private Context context; private int version = 0; public UpdaterPopupLauncher(Context ctxt, int onlineVersion) { context = ctxt; version = onlineVersion; } @Override public void run() { Builder ab = new AlertDialog.Builder(context); ab.setIcon(R.drawable.ic_launcher_nightly) .setTitle("Update nightly build"); if(version > 0) { ab.setMessage("Revision " + version + " available. Upgrade now?") .setPositiveButton(R.string.ok, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent it = new Intent(); it.setData(Uri.parse(BASE_UPDATE_VERSION + channel + "/CSipSimple-r"+version+"-" + channel + ".apk")); it.setClass(context, Downloader.class); it.putExtra(Downloader.EXTRA_ICON, R.drawable.ic_launcher_nightly); it.putExtra(Downloader.EXTRA_TITLE, "CSipSimple nightly build"); it.putExtra(Downloader.EXTRA_CHECK_MD5, true); it.putExtra(Downloader.EXTRA_OUTPATH, getCachedFile().getAbsolutePath()); Intent resultIntent = new Intent(context, DeviceStateReceiver.class); resultIntent.setAction(DeviceStateReceiver.APPLY_NIGHTLY_UPLOAD); resultIntent.putExtra(DOWNLOADED_VERSION, version); PendingIntent pi = PendingIntent.getBroadcast(context, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT); it.putExtra(Downloader.EXTRA_PENDING_FINISH_INTENT, pi); context.startService(it); } }) .setNegativeButton(R.string.cancel, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Editor edt = prefs.edit(); edt.putBoolean(IGNORE_NIGHTLY_CHECK, true); edt.commit(); dialog.dismiss(); } }); }else { ab.setMessage("No update available") .setPositiveButton(R.string.ok, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); } ab.create().show(); } } public void applyUpdate(Intent i) { File f = getCachedFile(); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(f.getAbsoluteFile()), "application/vnd.android.package-archive"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } }