From ef5084036cf2a21dfdfdb2f18b655900292ba51c Mon Sep 17 00:00:00 2001
From: Isira Seneviratne <isirasen96@gmail.com>
Date: Mon, 12 Oct 2020 06:25:35 +0530
Subject: [PATCH] Use RxJava instead of AsyncTask to check for new app
 versions.

---
 app/src/main/java/org/schabi/newpipe/App.java |  14 +-
 ...onTask.java => CheckForNewAppVersion.java} | 201 +++++++++---------
 .../settings/MainSettingsFragment.java        |   5 +-
 3 files changed, 120 insertions(+), 100 deletions(-)
 rename app/src/main/java/org/schabi/newpipe/{CheckForNewAppVersionTask.java => CheckForNewAppVersion.java} (58%)

diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java
index a94acda8e..5fdc1058a 100644
--- a/app/src/main/java/org/schabi/newpipe/App.java
+++ b/app/src/main/java/org/schabi/newpipe/App.java
@@ -36,6 +36,7 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
+import io.reactivex.disposables.Disposable;
 import io.reactivex.exceptions.CompositeException;
 import io.reactivex.exceptions.MissingBackpressureException;
 import io.reactivex.exceptions.OnErrorNotImplementedException;
@@ -65,6 +66,9 @@ public class App extends MultiDexApplication {
     protected static final String TAG = App.class.toString();
     private static App app;
 
+    private Disposable disposable = null;
+
+    @NonNull
     public static App getApp() {
         return app;
     }
@@ -100,7 +104,15 @@ public class App extends MultiDexApplication {
         configureRxJavaErrorHandler();
 
         // Check for new version
-        new CheckForNewAppVersionTask().execute();
+        disposable = CheckForNewAppVersion.checkNewVersion(this);
+    }
+
+    @Override
+    public void onTerminate() {
+        if (disposable != null) {
+            disposable.dispose();
+        }
+        super.onTerminate();
     }
 
     protected Downloader getDownloader() {
diff --git a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java
similarity index 58%
rename from app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java
rename to app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java
index 10a6a73d7..a193149e2 100644
--- a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java
+++ b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java
@@ -9,9 +9,9 @@ import android.content.pm.PackageManager;
 import android.content.pm.Signature;
 import android.net.ConnectivityManager;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.core.app.NotificationCompat;
 import androidx.core.app.NotificationManagerCompat;
 import androidx.core.content.ContextCompat;
@@ -35,16 +35,18 @@ import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 
-/**
- * AsyncTask to check if there is a newer version of the NewPipe github apk available or not.
- * If there is a newer version we show a notification, informing the user. On tapping
- * the notification, the user will be directed to the download link.
- */
-public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
-    private static final boolean DEBUG = MainActivity.DEBUG;
-    private static final String TAG = CheckForNewAppVersionTask.class.getSimpleName();
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.disposables.Disposables;
+import io.reactivex.schedulers.Schedulers;
+
+public final class CheckForNewAppVersion {
+    private CheckForNewAppVersion() { }
+
+    private static final boolean DEBUG = MainActivity.DEBUG;
+    private static final String TAG = CheckForNewAppVersion.class.getSimpleName();
 
-    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 NEWPIPE_API_URL = "https://newpipe.schabi.org/api/data.json";
@@ -52,18 +54,19 @@ public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
     /**
      * Method to get the apk's SHA1 key. See https://stackoverflow.com/questions/9293019/#22506133.
      *
+     * @param application The application
      * @return String with the apk's SHA1 fingeprint in hexadecimal
      */
-    private static String getCertificateSHA1Fingerprint() {
-        final PackageManager pm = APP.getPackageManager();
-        final String packageName = APP.getPackageName();
+    private static String getCertificateSHA1Fingerprint(@NonNull final Application application) {
+        final PackageManager pm = application.getPackageManager();
+        final String packageName = application.getPackageName();
         final int flags = PackageManager.GET_SIGNATURES;
         PackageInfo packageInfo = null;
 
         try {
             packageInfo = pm.getPackageInfo(packageName, flags);
         } catch (final PackageManager.NameNotFoundException e) {
-            ErrorActivity.reportError(APP, e, null, null,
+            ErrorActivity.reportError(application, e, null, null,
                     ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
                             "Could not find package info", R.string.app_ui_crash));
         }
@@ -78,7 +81,7 @@ public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
             final CertificateFactory cf = CertificateFactory.getInstance("X509");
             c = (X509Certificate) cf.generateCertificate(input);
         } catch (final CertificateException e) {
-            ErrorActivity.reportError(APP, e, null, null,
+            ErrorActivity.reportError(application, e, null, null,
                     ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
                             "Certificate error", R.string.app_ui_crash));
         }
@@ -90,7 +93,7 @@ public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
             final byte[] publicKey = md.digest(c.getEncoded());
             hexString = byte2HexFormatted(publicKey);
         } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
-            ErrorActivity.reportError(APP, e, null, null,
+            ErrorActivity.reportError(application, e, null, null,
                     ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
                             "Could not retrieve SHA1 key", R.string.app_ui_crash));
         }
@@ -118,104 +121,108 @@ public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
         return str.toString();
     }
 
-    public static boolean isGithubApk() {
-        return getCertificateSHA1Fingerprint().equals(GITHUB_APK_SHA1);
+    /**
+     * Method to compare the current and latest available app version.
+     * If a newer version is available, we show the update notification.
+     *
+     * @param application    The application
+     * @param versionName    Name of new version
+     * @param apkLocationUrl Url with the new apk
+     * @param versionCode    Code of new version
+     */
+    private static void compareAppVersionAndShowNotification(@NonNull final Application application,
+                                                             final String versionName,
+                                                             final String apkLocationUrl,
+                                                             final int versionCode) {
+        final int notificationId = 2000;
+
+        if (BuildConfig.VERSION_CODE < versionCode) {
+            // A pending intent to open the apk location url in the browser.
+            final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl));
+            final PendingIntent pendingIntent
+                    = PendingIntent.getActivity(application, 0, intent, 0);
+
+            final String channelId = application
+                    .getString(R.string.app_update_notification_channel_id);
+            final NotificationCompat.Builder notificationBuilder
+                    = new NotificationCompat.Builder(application, channelId)
+                    .setSmallIcon(R.drawable.ic_newpipe_update)
+                    .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+                    .setContentIntent(pendingIntent)
+                    .setAutoCancel(true)
+                    .setContentTitle(application
+                            .getString(R.string.app_update_notification_content_title))
+                    .setContentText(application
+                            .getString(R.string.app_update_notification_content_text)
+                            + " " + versionName);
+
+            final NotificationManagerCompat notificationManager
+                    = NotificationManagerCompat.from(application);
+            notificationManager.notify(notificationId, notificationBuilder.build());
+        }
     }
 
-    @Override
-    protected void onPreExecute() {
-        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(APP);
+    private static boolean isConnected(@NonNull final App app) {
+        final ConnectivityManager cm = ContextCompat.getSystemService(app,
+                ConnectivityManager.class);
+        return cm.getActiveNetworkInfo() != null
+                && cm.getActiveNetworkInfo().isConnected();
+    }
+
+    public static boolean isGithubApk(@NonNull final App app) {
+        return getCertificateSHA1Fingerprint(app).equals(GITHUB_APK_SHA1);
+    }
+
+    @NonNull
+    public static Disposable checkNewVersion(@NonNull final App app) {
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app);
 
         // Check if user has enabled/disabled update checking
         // and if the current apk is a github one or not.
-        if (!prefs.getBoolean(APP.getString(R.string.update_app_key), true) || !isGithubApk()) {
-            this.cancel(true);
-        }
-    }
-
-    @Override
-    protected String doInBackground(final Void... voids) {
-        if (isCancelled() || !isConnected()) {
-            return null;
+        if (!prefs.getBoolean(app.getString(R.string.update_app_key), true)
+                || !isGithubApk(app)) {
+            return Disposables.empty();
         }
 
-        // Make a network request to get latest NewPipe data.
-        try {
-            return DownloaderImpl.getInstance().get(NEWPIPE_API_URL).responseBody();
-        } catch (IOException | ReCaptchaException e) {
-            // connectivity problems, do not alarm user and fail silently
-            if (DEBUG) {
-                Log.w(TAG, Log.getStackTraceString(e));
+        return Observable.fromCallable(() -> {
+            if (!isConnected(app)) {
+                return null;
             }
-        }
-
-        return null;
-    }
-
-    @Override
-    protected void onPostExecute(final String response) {
-        // Parse the json from the response.
-        if (response != null) {
 
+            // Make a network request to get latest NewPipe data.
             try {
-                final JsonObject githubStableObject = JsonParser.object().from(response)
-                        .getObject("flavors").getObject("github").getObject("stable");
-
-                final String versionName = githubStableObject.getString("version");
-                final int versionCode = githubStableObject.getInt("version_code");
-                final String apkLocationUrl = githubStableObject.getString("apk");
-
-                compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode);
-
-            } catch (final JsonParserException e) {
+                return DownloaderImpl.getInstance().get(NEWPIPE_API_URL).responseBody();
+            } catch (IOException | ReCaptchaException e) {
                 // connectivity problems, do not alarm user and fail silently
                 if (DEBUG) {
                     Log.w(TAG, Log.getStackTraceString(e));
                 }
             }
-        }
-    }
 
-    /**
-     * Method to compare the current and latest available app version.
-     * If a newer version is available, we show the update notification.
-     *
-     * @param versionName    Name of new version
-     * @param apkLocationUrl Url with the new apk
-     * @param versionCode    Code of new version
-     */
-    private void compareAppVersionAndShowNotification(final String versionName,
-                                                      final String apkLocationUrl,
-                                                      final int versionCode) {
-        final int notificationId = 2000;
+            return null;
+        })
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(response -> {
+                    // Parse the json from the response.
+                    if (response != null) {
+                        try {
+                            final JsonObject githubStableObject = JsonParser.object().from(response)
+                                    .getObject("flavors").getObject("github").getObject("stable");
 
-        if (BuildConfig.VERSION_CODE < versionCode) {
+                            final String versionName = githubStableObject.getString("version");
+                            final int versionCode = githubStableObject.getInt("version_code");
+                            final String apkLocationUrl = githubStableObject.getString("apk");
 
-            // A pending intent to open the apk location url in the browser.
-            final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl));
-            final PendingIntent pendingIntent
-                    = PendingIntent.getActivity(APP, 0, intent, 0);
-
-            final 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);
-
-            final NotificationManagerCompat notificationManager
-                    = NotificationManagerCompat.from(APP);
-            notificationManager.notify(notificationId, notificationBuilder.build());
-        }
-    }
-
-    private boolean isConnected() {
-        final ConnectivityManager cm = ContextCompat.getSystemService(APP,
-                ConnectivityManager.class);
-        return cm.getActiveNetworkInfo() != null
-                && cm.getActiveNetworkInfo().isConnected();
+                            compareAppVersionAndShowNotification(app, versionName, apkLocationUrl,
+                                    versionCode);
+                        } catch (final JsonParserException e) {
+                            // connectivity problems, do not alarm user and fail silently
+                            if (DEBUG) {
+                                Log.w(TAG, Log.getStackTraceString(e));
+                            }
+                        }
+                    }
+                });
     }
 }
diff --git a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java
index 6d6f0fa75..2f65af4d6 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java
@@ -4,7 +4,8 @@ import android.os.Bundle;
 
 import androidx.preference.Preference;
 
-import org.schabi.newpipe.CheckForNewAppVersionTask;
+import org.schabi.newpipe.App;
+import org.schabi.newpipe.CheckForNewAppVersion;
 import org.schabi.newpipe.MainActivity;
 import org.schabi.newpipe.R;
 
@@ -15,7 +16,7 @@ public class MainSettingsFragment extends BasePreferenceFragment {
     public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
         addPreferencesFromResource(R.xml.main_settings);
 
-        if (!CheckForNewAppVersionTask.isGithubApk()) {
+        if (!CheckForNewAppVersion.isGithubApk(App.getApp())) {
             final Preference update = findPreference(getString(R.string.update_pref_screen_key));
             getPreferenceScreen().removePreference(update);