NewPipe/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java

239 lines
9.0 KiB
Java
Raw Normal View History

2018-08-11 16:06:23 +02:00
package org.schabi.newpipe;
import android.app.Application;
import android.app.PendingIntent;
2019-02-26 19:33:01 +01:00
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
2018-10-14 15:46:28 +02:00
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.ConnectivityManager;
import android.net.Uri;
2018-08-11 16:06:23 +02:00
import android.os.AsyncTask;
import android.preference.PreferenceManager;
2019-10-04 14:59:08 +02:00
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
2019-06-25 13:47:16 +02:00
import android.util.Log;
2018-08-11 16:06:23 +02:00
import org.json.JSONException;
import org.json.JSONObject;
2018-11-18 14:45:50 +01:00
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
2018-08-11 16:06:23 +02:00
2018-10-14 15:46:28 +02:00
import java.io.ByteArrayInputStream;
2018-08-11 16:06:23 +02:00
import java.io.IOException;
2018-10-14 15:46:28 +02:00
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
2018-11-18 14:45:50 +01:00
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
2018-08-11 16:06:23 +02:00
/**
2018-08-12 15:04:20 +02:00
* AsyncTask to check if there is a newer version of the NewPipe github apk available or not.
2018-08-11 16:06:23 +02:00
* If there is a newer version we show a notification, informing the user. On tapping
2018-08-12 15:04:20 +02:00
* the notification, the user will be directed to the download link.
2018-08-11 16:06:23 +02:00
*/
2018-08-12 15:04:20 +02:00
public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
2018-08-11 16:06:23 +02:00
2019-06-25 13:22:40 +02:00
private static final boolean DEBUG = MainActivity.DEBUG;
2019-06-25 13:31:26 +02:00
private static final String TAG = CheckForNewAppVersionTask.class.getSimpleName();
2018-11-18 14:45:50 +01:00
private static final Application app = App.getApp();
private static final String GITHUB_APK_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15";
private static final String newPipeApiUrl = "https://newpipe.schabi.org/api/data.json";
2018-11-18 14:45:50 +01:00
private static final int timeoutPeriod = 30;
2018-08-11 16:06:23 +02:00
private SharedPreferences mPrefs;
2018-11-18 14:45:50 +01:00
private OkHttpClient client;
2018-08-12 15:04:20 +02:00
@Override
protected void onPreExecute() {
mPrefs = PreferenceManager.getDefaultSharedPreferences(app);
2018-10-14 15:46:28 +02:00
// Check if user has enabled/ disabled update checking
// and if the current apk is a github one or not.
if (!mPrefs.getBoolean(app.getString(R.string.update_app_key), true)
|| !isGithubApk()) {
2018-08-16 22:16:33 +02:00
this.cancel(true);
2018-08-12 15:04:20 +02:00
}
}
2018-08-11 16:06:23 +02:00
@Override
protected String doInBackground(Void... voids) {
if(isCancelled() || !isConnected()) return null;
2018-08-11 16:06:23 +02:00
2018-08-12 15:04:20 +02:00
// Make a network request to get latest NewPipe data.
2018-11-18 14:45:50 +01:00
if (client == null) {
2018-08-11 16:06:23 +02:00
2018-11-18 14:45:50 +01:00
client = new OkHttpClient
.Builder()
.readTimeout(timeoutPeriod, TimeUnit.SECONDS)
.build();
}
2018-08-11 16:06:23 +02:00
2018-11-18 14:45:50 +01:00
Request request = new Request.Builder()
.url(newPipeApiUrl)
.build();
2018-08-11 16:06:23 +02:00
2018-11-18 14:45:50 +01:00
try {
Response response = client.newCall(request).execute();
return response.body().string();
2018-08-11 16:06:23 +02:00
} catch (IOException ex) {
// connectivity problems, do not alarm user and fail silently
2019-06-25 13:41:08 +02:00
if (DEBUG) Log.w(TAG, Log.getStackTraceString(ex));
2018-08-11 16:06:23 +02:00
}
return null;
}
@Override
2018-08-12 15:04:20 +02:00
protected void onPostExecute(String response) {
2018-08-11 16:06:23 +02:00
2018-08-12 15:04:20 +02:00
// Parse the json from the response.
if (response != null) {
2018-08-11 16:06:23 +02:00
try {
2018-08-12 15:04:20 +02:00
JSONObject mainObject = new JSONObject(response);
2018-08-11 16:06:23 +02:00
JSONObject flavoursObject = mainObject.getJSONObject("flavors");
JSONObject githubObject = flavoursObject.getJSONObject("github");
JSONObject githubStableObject = githubObject.getJSONObject("stable");
String versionName = githubStableObject.getString("version");
2018-08-16 21:23:42 +02:00
String versionCode = githubStableObject.getString("version_code");
2018-08-11 16:06:23 +02:00
String apkLocationUrl = githubStableObject.getString("apk");
2018-08-16 21:23:42 +02:00
compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode);
2018-08-11 16:06:23 +02:00
} catch (JSONException ex) {
2019-06-25 13:22:40 +02:00
// connectivity problems, do not alarm user and fail silently
2019-06-25 13:41:08 +02:00
if (DEBUG) Log.w(TAG, Log.getStackTraceString(ex));
2018-08-11 16:06:23 +02:00
}
}
}
/**
2018-08-12 15:04:20 +02:00
* Method to compare the current and latest available app version.
* If a newer version is available, we show the update notification.
* @param versionName
* @param apkLocationUrl
*/
2018-08-16 21:23:42 +02:00
private void compareAppVersionAndShowNotification(String versionName,
String apkLocationUrl,
String versionCode) {
2018-08-11 16:06:23 +02:00
int NOTIFICATION_ID = 2000;
2018-08-16 21:23:42 +02:00
if (BuildConfig.VERSION_CODE < Integer.valueOf(versionCode)) {
// A pending intent to open the apk location url in the browser.
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl));
PendingIntent pendingIntent
= PendingIntent.getActivity(app, 0, intent, 0);
NotificationCompat.Builder notificationBuilder = new NotificationCompat
.Builder(app, app.getString(R.string.app_update_notification_channel_id))
.setSmallIcon(R.drawable.ic_newpipe_update)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setContentTitle(app.getString(R.string.app_update_notification_content_title))
.setContentText(app.getString(R.string.app_update_notification_content_text)
+ " " + versionName);
2018-08-11 16:06:23 +02:00
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(app);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
2018-08-11 16:06:23 +02:00
}
}
2018-10-14 15:46:28 +02:00
/**
* Method to get the apk's SHA1 key.
* https://stackoverflow.com/questions/9293019/get-certificate-fingerprint-from-android-app#22506133
*/
private static String getCertificateSHA1Fingerprint() {
2018-10-14 15:46:28 +02:00
PackageManager pm = app.getPackageManager();
String packageName = app.getPackageName();
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, flags);
2018-11-18 14:48:16 +01:00
} catch (PackageManager.NameNotFoundException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not find package info", R.string.app_ui_crash));
2018-10-14 15:46:28 +02:00
}
Signature[] signatures = packageInfo.signatures;
byte[] cert = signatures[0].toByteArray();
InputStream input = new ByteArrayInputStream(cert);
2018-10-22 19:42:25 +02:00
CertificateFactory cf = null;
2018-10-14 15:46:28 +02:00
X509Certificate c = null;
try {
2018-10-22 19:42:25 +02:00
cf = CertificateFactory.getInstance("X509");
2018-10-14 15:46:28 +02:00
c = (X509Certificate) cf.generateCertificate(input);
2018-11-18 14:48:16 +01:00
} catch (CertificateException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Certificate error", R.string.app_ui_crash));
2018-10-14 15:46:28 +02:00
}
String hexString = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(c.getEncoded());
hexString = byte2HexFormatted(publicKey);
2018-11-18 14:45:50 +01:00
} catch (NoSuchAlgorithmException ex1) {
ErrorActivity.reportError(app, ex1, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not retrieve SHA1 key", R.string.app_ui_crash));
} catch (CertificateEncodingException ex2) {
ErrorActivity.reportError(app, ex2, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not retrieve SHA1 key", R.string.app_ui_crash));
2018-10-14 15:46:28 +02:00
}
return hexString;
}
private static String byte2HexFormatted(byte[] arr) {
StringBuilder str = new StringBuilder(arr.length * 2);
for (int i = 0; i < arr.length; i++) {
String h = Integer.toHexString(arr[i]);
int l = h.length();
if (l == 1) h = "0" + h;
if (l > 2) h = h.substring(l - 2, l);
str.append(h.toUpperCase());
if (i < (arr.length - 1)) str.append(':');
}
return str.toString();
}
public static boolean isGithubApk() {
2018-11-25 20:48:33 +01:00
return getCertificateSHA1Fingerprint().equals(GITHUB_APK_SHA1);
}
private boolean isConnected() {
ConnectivityManager cm =
(ConnectivityManager) app.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null
&& cm.getActiveNetworkInfo().isConnected();
}
2018-08-11 16:06:23 +02:00
}