Skip to content

Commit

Permalink
Merge pull request #120 from cpeterso/cpeterso/check-for-updates
Browse files Browse the repository at this point in the history
Download and install MozStumbler updates from GitHub
  • Loading branch information
cpeterso committed Oct 4, 2013
2 parents 66121ce + 5f23160 commit 4892cfb
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 0 deletions.
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.5.1
4 changes: 4 additions & 0 deletions res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
<string name="enter_nickname">Enter Nickname</string>
<string name="battery_low_warning">Battery low, MozStumbler will stop scanning</string>

<string name="update_title">Download MozStumbler Update?</string>
<string name="update_message">Installed version: %1$s\nLatest version: %2$s</string>
<string name="update_now">Update Now</string>
<string name="update_later">Update Later</string>

<string name="notice">blah legal blah blah</string>

Expand Down
2 changes: 2 additions & 0 deletions src/org/mozilla/mozstumbler/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ protected void onCreate(Bundle savedInstanceState) {
mPrefs = new Prefs(this);
if (!mPrefs.hasSeenNotice()) {
new NoticeDialog(this, mPrefs).show();
} else {
Updater.checkForUpdates(this);
}

EditText nicknameEditor = (EditText) findViewById(R.id.edit_nickname);
Expand Down
1 change: 1 addition & 0 deletions src/org/mozilla/mozstumbler/NoticeDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public void onCancel(DialogInterface di) {
public void onClick(DialogInterface di, int which) {
mPrefs.setHasSeenNotice();
di.dismiss();
Updater.checkForUpdates(mActivity);
}
})
.setNegativeButton(android.R.string.cancel,
Expand Down
261 changes: 261 additions & 0 deletions src/org/mozilla/mozstumbler/Updater.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
package org.mozilla.mozstumbler;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

final class Updater {
private static final String LOGTAG = Updater.class.getName();
private static final String VERSION_URL = "https://raw.github.com/dougt/MozStumbler/master/VERSION";
private static final String APK_URL_FORMAT = "https://github.com/dougt/MozStumbler/releases/download/v%s/MozStumbler-v%s.apk";

private Updater() {
}

static void checkForUpdates(final Context context) {
new AsyncTask<Void, Void, String>() {
@Override
public String doInBackground(Void... params) {
try {
URL url = new URL(VERSION_URL);
InputStream stream = null;
try {
URLConnection connection = openConnectionWithProxy(url);
stream = connection.getInputStream();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(stream));
return reader.readLine();
} finally {
if (reader != null) {
reader.close();
}
}
} finally {
if (stream != null) {
stream.close();
}
}
} catch (FileNotFoundException fe) {
Log.w(LOGTAG, "VERSION not found: " + VERSION_URL);
} catch (IOException e) {
Log.e(LOGTAG, "", e);
}

return null;
}

@Override
public void onPostExecute(String latestVersion) {
String installedVersion = PackageUtils.getAppVersion(context);

Log.d(LOGTAG, "Installed version: " + installedVersion);
Log.d(LOGTAG, "Latest version: " + latestVersion);

if (isVersionGreaterThan(latestVersion, installedVersion)) {
showUpdateDialog(context, installedVersion, latestVersion);
}
}
}.execute();
}

private static boolean isVersionGreaterThan(String a, String b) {
if (a == null) {
return false;
}

if (b == null) {
return true;
}

if (a.equals(b)) {
return false; // fast path
}

String[] as = a.split("\\.");
String[] bs = b.split("\\.");
int len = Math.min(as.length, bs.length);

try {
for (int i = 0; i < len; i++) {
int an = Integer.parseInt(as[i]);
int bn = Integer.parseInt(bs[i]);
if (an == bn) {
continue;
}
return (an > bn);
}
} catch (NumberFormatException e) {
Log.w(LOGTAG, "a='" + a + "', b='" + b + "'", e);
return false;
}

// Strings have identical prefixes, so longest version string wins.
return as.length > bs.length;
}

private static void showUpdateDialog(final Context context,
String installedVersion,
final String latestVersion) {
String msg = context.getString(R.string.update_message);
msg = String.format(msg, installedVersion, latestVersion);

final Dialog.OnCancelListener onCancel = new Dialog.OnCancelListener() {
@Override
public void onCancel(DialogInterface di) {
di.dismiss();
}
};

AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.update_title))
.setMessage(msg)
.setPositiveButton(context.getString(R.string.update_now),
new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface di, int which) {
Log.d(LOGTAG, "Update Now");
di.dismiss();
downloadAndInstallUpdate(context, latestVersion);
}
})
.setNegativeButton(context.getString(R.string.update_later),
new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface di, int which) {
onCancel.onCancel(di);
}
})
.setOnCancelListener(onCancel);
builder.create().show();
}

private static void downloadAndInstallUpdate(final Context context, final String version) {
new AsyncTask<Void, Void, Void>() {
@Override
public Void doInBackground(Void... params) {
URL apkURL = getUpdateURL(version);
File apk = downloadFile(context, apkURL);
if (apk == null || !apk.exists()) {
Log.e(LOGTAG, "Update file not found!");
return null;
}

installPackage(context, apk);
return null;
}
}.execute();
}

private static URL getUpdateURL(String version) {
String url = String.format(APK_URL_FORMAT, version, version);
try {
return new URL(url);
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
}

private static File downloadFile(Context context, URL url) {
Log.d(LOGTAG, "Downloading: " + url);

File file = createTempFile(context);
if (file == null) {
return null;
}

final int bufferLength = 8192;
final byte[] buffer = new byte[bufferLength];

try {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
URLConnection connection = openConnectionWithProxy(url);
inputStream = connection.getInputStream();
outputStream = new FileOutputStream(file);
for (;;) {
int readLength = inputStream.read(buffer, 0, bufferLength);
if (readLength == -1) {
return file;
}
outputStream.write(buffer, 0, readLength);
}
} finally {
if (outputStream != null) {
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
}
} catch (IOException e) {
Log.e(LOGTAG, "", e);
file.delete();
return null;
}
}

private static URLConnection openConnectionWithProxy(URL url) throws IOException {
Proxy proxy = Proxy.NO_PROXY;

ProxySelector proxySelector = ProxySelector.getDefault();
if (proxySelector != null) {
URI uri;
try {
uri = url.toURI();
} catch (URISyntaxException e) {
throw new IOException(url.toString(), e);
}

List<Proxy> proxies = proxySelector.select(uri);
if (proxies != null && !proxies.isEmpty()) {
proxy = proxies.get(0);
}
}

return url.openConnection(proxy);
}

private static File createTempFile(Context context) {
File dir = context.getExternalFilesDir(null);
try {
return File.createTempFile("update", ".apk", dir);
} catch (IOException e) {
Log.e(LOGTAG, "", e);
return null;
}
}

private static void installPackage(Context context, File apkFile) {
Uri apkURI = Uri.fromFile(apkFile);
Log.d(LOGTAG, "Installing: " + apkURI);

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkURI, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}

0 comments on commit 4892cfb

Please sign in to comment.