mirror of https://github.com/M66B/FairEmail.git
parent
eb814992b7
commit
19eaced6ad
@ -0,0 +1,274 @@
|
|||||||
|
package eu.faircode.email;
|
||||||
|
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
|
||||||
|
public class Update {
|
||||||
|
private static final int UPDATE_TIMEOUT = 15 * 1000; // milliseconds
|
||||||
|
static final long UPDATE_DAILY = (BuildConfig.BETA_RELEASE ? 4 : 12) * 3600 * 1000L; // milliseconds
|
||||||
|
static final long UPDATE_WEEKLY = 7 * 24 * 3600 * 1000L; // milliseconds
|
||||||
|
|
||||||
|
static void check(ActivityBase context, boolean always) {
|
||||||
|
if (Helper.isPlayStoreInstall())
|
||||||
|
return;
|
||||||
|
if (!Helper.hasValidFingerprint(context) && !(always && BuildConfig.DEBUG))
|
||||||
|
return;
|
||||||
|
|
||||||
|
long now = new Date().getTime();
|
||||||
|
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
boolean updates = prefs.getBoolean("updates", true);
|
||||||
|
boolean beta = prefs.getBoolean("beta", false) && false;
|
||||||
|
boolean weekly = prefs.getBoolean("weekly", Helper.hasPlayStore(context));
|
||||||
|
long last_update_check = prefs.getLong("last_update_check", 0);
|
||||||
|
|
||||||
|
if (!always && !updates)
|
||||||
|
return;
|
||||||
|
if (!always && last_update_check + (weekly ? UPDATE_WEEKLY : UPDATE_DAILY) > now)
|
||||||
|
return;
|
||||||
|
|
||||||
|
prefs.edit().putLong("last_update_check", now).apply();
|
||||||
|
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putBoolean("always", always);
|
||||||
|
args.putBoolean("beta", beta);
|
||||||
|
|
||||||
|
new SimpleTask<Info>() {
|
||||||
|
@Override
|
||||||
|
protected Info onExecute(Context context, Bundle args) throws Throwable {
|
||||||
|
boolean beta = args.getBoolean("beta");
|
||||||
|
|
||||||
|
StringBuilder response = new StringBuilder();
|
||||||
|
HttpsURLConnection urlConnection = null;
|
||||||
|
try {
|
||||||
|
URL latest = new URL(beta ? BuildConfig.BITBUCKET_DOWNLOADS_API : BuildConfig.GITHUB_LATEST_API);
|
||||||
|
urlConnection = (HttpsURLConnection) latest.openConnection();
|
||||||
|
urlConnection.setRequestMethod("GET");
|
||||||
|
urlConnection.setReadTimeout(UPDATE_TIMEOUT);
|
||||||
|
urlConnection.setConnectTimeout(UPDATE_TIMEOUT);
|
||||||
|
urlConnection.setDoOutput(false);
|
||||||
|
ConnectionHelper.setUserAgent(context, urlConnection);
|
||||||
|
urlConnection.connect();
|
||||||
|
|
||||||
|
int status = urlConnection.getResponseCode();
|
||||||
|
InputStream inputStream = (status == HttpsURLConnection.HTTP_OK
|
||||||
|
? urlConnection.getInputStream() : urlConnection.getErrorStream());
|
||||||
|
|
||||||
|
if (inputStream != null) {
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null)
|
||||||
|
response.append(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == HttpsURLConnection.HTTP_FORBIDDEN) {
|
||||||
|
// {"message":"API rate limit exceeded for ...","documentation_url":"https://developer.github.com/v3/#rate-limiting"}
|
||||||
|
JSONObject jmessage = new JSONObject(response.toString());
|
||||||
|
if (jmessage.has("message"))
|
||||||
|
throw new IllegalArgumentException(jmessage.getString("message"));
|
||||||
|
throw new IOException("HTTP " + status + ": " + response);
|
||||||
|
}
|
||||||
|
if (status != HttpsURLConnection.HTTP_OK)
|
||||||
|
throw new IOException("HTTP " + status + ": " + response);
|
||||||
|
|
||||||
|
JSONObject jroot = new JSONObject(response.toString());
|
||||||
|
|
||||||
|
if (beta) {
|
||||||
|
if (!jroot.has("values"))
|
||||||
|
throw new IOException("values field missing");
|
||||||
|
|
||||||
|
JSONArray jvalues = jroot.getJSONArray("values");
|
||||||
|
for (int i = 0; i < jvalues.length(); i++) {
|
||||||
|
JSONObject jitem = jvalues.getJSONObject(i);
|
||||||
|
if (!jitem.has("links"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
JSONObject jlinks = jitem.getJSONObject("links");
|
||||||
|
if (!jlinks.has("self"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
JSONObject jself = jlinks.getJSONObject("self");
|
||||||
|
if (!jself.has("href"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// .../FairEmail-v1.1995a-play-preview-release.apk
|
||||||
|
String link = jself.getString("href");
|
||||||
|
if (!link.endsWith(".apk"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int slash = link.lastIndexOf('/');
|
||||||
|
if (slash < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String[] c = link.substring(slash + 1).split("-");
|
||||||
|
if (c.length < 4 ||
|
||||||
|
!"FairEmail".equals(c[0]) ||
|
||||||
|
c[1].length() < 8 ||
|
||||||
|
!"github".equals(c[2]) ||
|
||||||
|
!"update".equals(c[3]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// v1.1995a
|
||||||
|
Integer version = Helper.parseInt(c[1].substring(3, c[1].length() - 1));
|
||||||
|
if (version == null)
|
||||||
|
continue;
|
||||||
|
char revision = c[1].charAt(c[1].length() - 1);
|
||||||
|
|
||||||
|
int v = BuildConfig.VERSION_CODE;
|
||||||
|
char r = BuildConfig.REVISION.charAt(0);
|
||||||
|
if (BuildConfig.DEBUG || version > v || (version == v && revision > r)) {
|
||||||
|
Info info = new Info();
|
||||||
|
info.tag_name = c[1];
|
||||||
|
info.html_url = BuildConfig.BITBUCKET_DOWNLOADS_URI;
|
||||||
|
info.download_url = link;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!jroot.has("tag_name") || jroot.isNull("tag_name"))
|
||||||
|
throw new IOException("tag_name field missing");
|
||||||
|
if (!jroot.has("assets") || jroot.isNull("assets"))
|
||||||
|
throw new IOException("assets section missing");
|
||||||
|
|
||||||
|
// Get update info
|
||||||
|
Info info = new Info();
|
||||||
|
info.tag_name = jroot.getString("tag_name");
|
||||||
|
info.html_url = BuildConfig.GITHUB_LATEST_URI;
|
||||||
|
|
||||||
|
// Check if new release
|
||||||
|
JSONArray jassets = jroot.getJSONArray("assets");
|
||||||
|
for (int i = 0; i < jassets.length(); i++) {
|
||||||
|
JSONObject jasset = jassets.getJSONObject(i);
|
||||||
|
if (jasset.has("name") && !jasset.isNull("name")) {
|
||||||
|
String name = jasset.getString("name");
|
||||||
|
if (name.endsWith(".apk") && name.contains("github")) {
|
||||||
|
info.download_url = jasset.optString("browser_download_url");
|
||||||
|
Log.i("Latest version=" + info.tag_name);
|
||||||
|
if (BuildConfig.DEBUG)
|
||||||
|
return info;
|
||||||
|
try {
|
||||||
|
if (Double.parseDouble(info.tag_name) <=
|
||||||
|
Double.parseDouble(BuildConfig.VERSION_NAME))
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return info;
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
Log.e(ex);
|
||||||
|
if (BuildConfig.VERSION_NAME.equals(info.tag_name))
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (urlConnection != null)
|
||||||
|
urlConnection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onExecuted(Bundle args, Info info) {
|
||||||
|
boolean always = args.getBoolean("always");
|
||||||
|
if (info == null) {
|
||||||
|
if (always)
|
||||||
|
ToastEx.makeText(context, R.string.title_no_update, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder =
|
||||||
|
new NotificationCompat.Builder(context, "update")
|
||||||
|
.setSmallIcon(R.drawable.baseline_get_app_white_24)
|
||||||
|
.setContentTitle(context.getString(R.string.title_updated, info.tag_name))
|
||||||
|
.setContentText(info.html_url)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setShowWhen(false)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_REMINDER)
|
||||||
|
.setVisibility(NotificationCompat.VISIBILITY_SECRET);
|
||||||
|
|
||||||
|
Intent update = new Intent(Intent.ACTION_VIEW, Uri.parse(info.html_url))
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
PendingIntent piUpdate = PendingIntentCompat.getActivity(
|
||||||
|
context, ActivityView.PI_UPDATE, update, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
builder.setContentIntent(piUpdate);
|
||||||
|
|
||||||
|
Intent manage = new Intent(context, ActivitySetup.class)
|
||||||
|
.setAction("misc")
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
.putExtra("tab", "misc");
|
||||||
|
PendingIntent piManage = PendingIntentCompat.getActivity(
|
||||||
|
context, ActivitySetup.PI_MISC, manage, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
NotificationCompat.Action.Builder actionManage = new NotificationCompat.Action.Builder(
|
||||||
|
R.drawable.twotone_settings_24,
|
||||||
|
context.getString(R.string.title_setup_manage),
|
||||||
|
piManage);
|
||||||
|
builder.addAction(actionManage.build());
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(info.download_url)) {
|
||||||
|
Intent download = new Intent(Intent.ACTION_VIEW, Uri.parse(info.download_url))
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
PendingIntent piDownload = PendingIntentCompat.getActivity(
|
||||||
|
context, 0, download, 0);
|
||||||
|
NotificationCompat.Action.Builder actionDownload = new NotificationCompat.Action.Builder(
|
||||||
|
R.drawable.twotone_cloud_download_24,
|
||||||
|
context.getString(R.string.title_download),
|
||||||
|
piDownload);
|
||||||
|
builder.addAction(actionDownload.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
NotificationManager nm =
|
||||||
|
Helper.getSystemService(context, NotificationManager.class);
|
||||||
|
if (NotificationHelper.areNotificationsEnabled(nm))
|
||||||
|
nm.notify(NotificationHelper.NOTIFICATION_UPDATE,
|
||||||
|
builder.build());
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
Log.w(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onException(Bundle args, Throwable ex) {
|
||||||
|
if (args.getBoolean("always"))
|
||||||
|
if (ex instanceof IllegalArgumentException || ex instanceof IOException)
|
||||||
|
ToastEx.makeText(context, ex.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
|
else
|
||||||
|
Log.unexpectedError(context.getSupportFragmentManager(), ex);
|
||||||
|
}
|
||||||
|
}.execute(context, args, "update:check");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Info {
|
||||||
|
String tag_name; // version
|
||||||
|
String html_url;
|
||||||
|
String download_url;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue