From 0f1aadc4c7e442f83027a2200dc06321f7a1f5dc Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Sun, 3 Feb 2019 20:58:32 +0100
Subject: [PATCH] Fixed debug builds not working on versions < 21

---
 app/build.gradle.kts                          |  16 +-
 .../de/kuschku/quasseldroid/Quasseldroid.kt   | 217 +-----------------
 .../kuschku/quasseldroid/app/AppDelegate.kt   |  27 +++
 .../app/QuasseldroidReleaseDelegate.kt        | 214 +++++++++++++++++
 .../kuschku/quasseldroid/QuasseldroidTest.kt  |   6 +-
 .../app/QuasseldroidTestDelegate.kt           |  27 +++
 6 files changed, 286 insertions(+), 221 deletions(-)
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/app/AppDelegate.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/app/QuasseldroidReleaseDelegate.kt
 create mode 100644 app/src/test/java/de/kuschku/quasseldroid/app/QuasseldroidTestDelegate.kt

diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 1bc233e75..907d1f2e6 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -57,8 +57,6 @@ android {
 
     setProperty("archivesBaseName", "Quasseldroid-$versionName")
 
-    multiDexEnabled = false
-
     // Disable test runner analytics
     testInstrumentationRunnerArguments = mapOf(
       "disableAnalytics" to "true"
@@ -72,6 +70,8 @@ android {
       isShrinkResources = true
       isUseProguard = false
 
+      multiDexEnabled = false
+
       proguardFiles(
         getDefaultProguardFile("proguard-android.txt"),
         "proguard-rules.pro"
@@ -81,15 +81,7 @@ android {
     getByName("debug") {
       applicationIdSuffix = ".debug"
 
-      isZipAlignEnabled = true
-      isMinifyEnabled = true
-      isShrinkResources = true
-      isUseProguard = false
-
-      proguardFiles(
-        getDefaultProguardFile("proguard-android.txt"),
-        "proguard-rules.pro"
-      )
+      multiDexEnabled = true
     }
   }
 
@@ -134,6 +126,8 @@ dependencies {
 
   implementation("androidx.paging", "paging-runtime", "2.0.0-rc01")
 
+  implementation("androidx.multidex", "multidex", "2.0.0")
+
   // Utility
   implementation("io.reactivex.rxjava2", "rxandroid", "2.0.2")
   implementation("io.reactivex.rxjava2", "rxjava", "2.1.9")
diff --git a/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt b/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt
index 505d431ca..747366f5b 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt
@@ -20,224 +20,27 @@
 package de.kuschku.quasseldroid
 
 import android.content.Context
-import android.os.Build
-import android.os.StrictMode
-import com.squareup.leakcanary.LeakCanary
-import dagger.android.AndroidInjector
 import dagger.android.support.DaggerApplication
-import de.kuschku.malheur.CrashHandler
+import de.kuschku.quasseldroid.app.AppDelegate
+import de.kuschku.quasseldroid.app.QuasseldroidReleaseDelegate
 import de.kuschku.quasseldroid.dagger.DaggerAppComponent
-import de.kuschku.quasseldroid.persistence.db.AccountDatabase
-import de.kuschku.quasseldroid.persistence.db.LegacyAccountDatabase
-import de.kuschku.quasseldroid.persistence.models.Account
-import de.kuschku.quasseldroid.settings.AppearanceSettings
-import de.kuschku.quasseldroid.settings.SettingsMigration
-import de.kuschku.quasseldroid.settings.SettingsMigrationManager
-import de.kuschku.quasseldroid.util.backport.AndroidThreeTenBackport
-import de.kuschku.quasseldroid.util.compatibility.AndroidCompatibilityUtils
-import de.kuschku.quasseldroid.util.compatibility.AndroidLoggingHandler
-import de.kuschku.quasseldroid.util.compatibility.AndroidStreamChannelFactory
 import de.kuschku.quasseldroid.util.ui.LocaleHelper
 
 open class Quasseldroid : DaggerApplication() {
-  override fun applicationInjector(): AndroidInjector<Quasseldroid> =
-    DaggerAppComponent.builder().create(this)
-
-  protected open fun init() {
-    if (LeakCanary.isInAnalyzerProcess(this)) {
-      // This process is dedicated to LeakCanary for heap analysis.
-      // You should not init your app in this process.
-      return
-    }
-    LeakCanary.install(this)
-    // Normal app init code...
-
-    CrashHandler.init(
-      application = this,
-      buildConfig = BuildConfig::class.java
-    )
-
-    // Init compatibility utils
-    AndroidCompatibilityUtils.inject()
-    AndroidLoggingHandler.inject()
-    AndroidStreamChannelFactory.inject()
-
-    AndroidThreeTenBackport.init(this)
-
-    applicationInjector().inject(this)
-
-    // Migrate preferences
-    SettingsMigrationManager(
-      listOf(
-        SettingsMigration.migrationOf(0, 1) { prefs, edit ->
-          // Migrating database
-          val database = LegacyAccountDatabase.Creator.init(this)
-          val accounts = database.accounts().all()
-          database.close()
-
-          val accountDatabase = AccountDatabase.Creator.init(this)
-          accountDatabase.accounts().create(*accounts.map {
-            Account(
-              id = it.id,
-              host = it.host,
-              port = it.port,
-              user = it.user,
-              requireSsl = false,
-              pass = it.pass,
-              name = it.name,
-              lastUsed = 0,
-              acceptedMissingFeatures = false,
-              defaultFiltered = 0
-            )
-          }.toTypedArray())
-          Thread(Runnable {
-            deleteDatabase("data")
-          }).start()
-
-          // Migrating actual settings
-          if (prefs.contains("selectedtheme")) {
-            prefs.getString("selectedtheme", "").let { theme ->
-              when (theme) {
-                "light" -> AppearanceSettings.Theme.MATERIAL_LIGHT
-                "dark"  -> AppearanceSettings.Theme.MATERIAL_DARK
-                else    -> null
-              }?.let {
-                edit.putString(getString(R.string.preference_theme_key), it.name)
-              }
-            }
-            edit.remove("selectedtheme")
-          }
-
-          if (prefs.contains("timestamp")) {
-            prefs.getString("timestamp", "").let {
-              val timestamp = it ?: ""
-              edit.putBoolean(getString(R.string.preference_show_seconds_key),
-                              timestamp.contains("ss"))
-              edit.putBoolean(getString(R.string.preference_show_seconds_key),
-                              !timestamp.contains("hh") && !timestamp.contains("a"))
-            }
-            edit.remove("timestamp")
-          }
-
-          if (prefs.contains("fontsizeChannelList")) {
-            prefs.getString("fontsizeChannelList", "")?.toIntOrNull()?.let { fontSize ->
-              edit.putInt(getString(R.string.preference_textsize_key), fontSize)
-            }
-            edit.remove("fontsizeChannelList")
-          }
-
-          if (prefs.contains("allowcoloredtext")) {
-            prefs.getBoolean("allowcoloredtext", false).let {
-              edit.putBoolean(getString(R.string.preference_colorize_mirc_key), it)
-            }
-            edit.remove("allowcoloredtext")
-          }
-
-          if (prefs.contains("monospace")) {
-            prefs.getBoolean("monospace", false).let {
-              edit.putBoolean(getString(R.string.preference_monospace_key), it)
-            }
-            edit.remove("monospace")
-          }
-
-          if (prefs.contains("detailed_actions")) {
-            prefs.getBoolean("detailed_actions", false).let {
-              edit.putBoolean(getString(R.string.preference_hostmask_actions_key), it)
-            }
-            edit.remove("detailed_actions")
-          }
-
-          if (prefs.contains("showlag")) {
-            prefs.getBoolean("showlag", false).let {
-              edit.putBoolean(getString(R.string.preference_show_lag_key), it)
-            }
-            edit.remove("showlag")
-          }
-        }
-      )
-    ).migrate(this)
-
-    // Initialize preferences unless already set
-
-    /*
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-      systemService<ShortcutManager>().dynamicShortcuts = listOf(
-        ShortcutInfo.Builder(this, "id1")
-          .setShortLabel("#quassel")
-          .setIcon(Icon.createWithResource(this, R.drawable.ic_shortcut_channel))
-          .setIntent(packageManager.getLaunchIntentForPackage(BuildConfig.APPLICATION_ID))
-          .build(),
-        ShortcutInfo.Builder(this, "id2")
-          .setShortLabel("#quasseldroid")
-          .setIcon(Icon.createWithResource(this, R.drawable.ic_shortcut_channel))
-          .setIntent(packageManager.getLaunchIntentForPackage(BuildConfig.APPLICATION_ID))
-          .build(),
-        ShortcutInfo.Builder(this, "id3")
-          .setShortLabel("#quassel.de")
-          .setIcon(Icon.createWithResource(this, R.drawable.ic_shortcut_channel))
-          .setIntent(packageManager.getLaunchIntentForPackage(BuildConfig.APPLICATION_ID))
-          .build(),
-        ShortcutInfo.Builder(this, "id4")
-          .setShortLabel("justJanne")
-          .setIcon(Icon.createWithResource(this, R.drawable.ic_shortcut_query))
-          .setIntent(packageManager.getLaunchIntentForPackage(BuildConfig.APPLICATION_ID))
-          .build()
-      )
-    }
-    */
-
-    if (BuildConfig.DEBUG) {
-      StrictMode.setThreadPolicy(
-        StrictMode.ThreadPolicy.Builder()
-          .detectNetwork()
-          .detectCustomSlowCalls()
-          .let {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-              it.detectResourceMismatches()
-            } else {
-              it
-            }
-          }.let {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-              it.detectUnbufferedIo()
-            } else {
-              it
-            }
-          }
-          .penaltyLog()
-          .build()
-      )
-      StrictMode.setVmPolicy(
-        StrictMode.VmPolicy.Builder()
-          .detectLeakedSqlLiteObjects()
-          .detectActivityLeaks()
-          .detectLeakedClosableObjects()
-          .detectLeakedRegistrationObjects()
-          .let {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-              it.detectFileUriExposure()
-            } else {
-              it
-            }
-          }.let {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-              it.detectContentUriWithoutPermission()
-            } else {
-              it
-            }
-          }
-          .penaltyLog()
-          .build()
-      )
-    }
-  }
+  override fun applicationInjector() = DaggerAppComponent.builder().create(this)
+  open val delegate: AppDelegate = QuasseldroidReleaseDelegate(this)
 
   override fun onCreate() {
     super.onCreate()
-    init()
+    if (delegate.shouldInit()) {
+      delegate.onInit()
+      applicationInjector().inject(this)
+      delegate.onPostInit()
+    }
   }
 
   override fun attachBaseContext(base: Context) {
     super.attachBaseContext(LocaleHelper.setLocale(base))
+    delegate.onInstallMultidex()
   }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/app/AppDelegate.kt b/app/src/main/java/de/kuschku/quasseldroid/app/AppDelegate.kt
new file mode 100644
index 000000000..482bb3550
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/app/AppDelegate.kt
@@ -0,0 +1,27 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.app
+
+interface AppDelegate {
+  fun shouldInit(): Boolean
+  fun onInstallMultidex()
+  fun onInit()
+  fun onPostInit()
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/app/QuasseldroidReleaseDelegate.kt b/app/src/main/java/de/kuschku/quasseldroid/app/QuasseldroidReleaseDelegate.kt
new file mode 100644
index 000000000..87724c040
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/app/QuasseldroidReleaseDelegate.kt
@@ -0,0 +1,214 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.app
+
+import android.os.Build
+import android.os.StrictMode
+import androidx.multidex.MultiDex
+import com.squareup.leakcanary.LeakCanary
+import de.kuschku.malheur.CrashHandler
+import de.kuschku.quasseldroid.BuildConfig
+import de.kuschku.quasseldroid.Quasseldroid
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.persistence.db.AccountDatabase
+import de.kuschku.quasseldroid.persistence.db.LegacyAccountDatabase
+import de.kuschku.quasseldroid.persistence.models.Account
+import de.kuschku.quasseldroid.settings.AppearanceSettings
+import de.kuschku.quasseldroid.settings.SettingsMigration
+import de.kuschku.quasseldroid.settings.SettingsMigrationManager
+import de.kuschku.quasseldroid.util.backport.AndroidThreeTenBackport
+import de.kuschku.quasseldroid.util.compatibility.AndroidCompatibilityUtils
+import de.kuschku.quasseldroid.util.compatibility.AndroidLoggingHandler
+import de.kuschku.quasseldroid.util.compatibility.AndroidStreamChannelFactory
+
+class QuasseldroidReleaseDelegate(private val app: Quasseldroid) :
+  AppDelegate {
+  override fun shouldInit() = !LeakCanary.isInAnalyzerProcess(app)
+
+  override fun onInit() {
+    LeakCanary.install(app)
+    // Normal app init code...
+
+    CrashHandler.init(
+      application = app,
+      buildConfig = BuildConfig::class.java
+    )
+
+    // Init compatibility utils
+    AndroidCompatibilityUtils.inject()
+    AndroidLoggingHandler.inject()
+    AndroidStreamChannelFactory.inject()
+
+    AndroidThreeTenBackport.init(app)
+  }
+
+  override fun onPostInit() {
+    // Migrate preferences
+    SettingsMigrationManager(
+      listOf(
+        SettingsMigration.migrationOf(0,
+                                      1) { prefs, edit ->
+          // Migrating database
+          val database = LegacyAccountDatabase.Creator.init(
+            app)
+          val accounts = database.accounts().all()
+          database.close()
+
+          val accountDatabase = AccountDatabase.Creator.init(
+            app)
+          accountDatabase.accounts().create(*accounts.map {
+            Account(
+              id = it.id,
+              host = it.host,
+              port = it.port,
+              user = it.user,
+              requireSsl = false,
+              pass = it.pass,
+              name = it.name,
+              lastUsed = 0,
+              acceptedMissingFeatures = false,
+              defaultFiltered = 0
+            )
+          }.toTypedArray())
+          Thread(Runnable {
+            app.deleteDatabase("data")
+          }).start()
+
+          // Migrating actual settings
+          if (prefs.contains("selectedtheme")) {
+            prefs.getString("selectedtheme", "").let { theme ->
+              when (theme) {
+                "light" -> AppearanceSettings.Theme.MATERIAL_LIGHT
+                "dark"  -> AppearanceSettings.Theme.MATERIAL_DARK
+                else    -> null
+              }?.let {
+                edit.putString(app.getString(R.string.preference_theme_key),
+                               it.name)
+              }
+            }
+            edit.remove("selectedtheme")
+          }
+
+          if (prefs.contains("timestamp")) {
+            prefs.getString("timestamp", "").let {
+              val timestamp = it ?: ""
+              edit.putBoolean(app.getString(R.string.preference_show_seconds_key),
+                              timestamp.contains("ss"))
+              edit.putBoolean(app.getString(R.string.preference_show_seconds_key),
+                              !timestamp.contains("hh") && !timestamp.contains("a"))
+            }
+            edit.remove("timestamp")
+          }
+
+          if (prefs.contains("fontsizeChannelList")) {
+            prefs.getString("fontsizeChannelList", "")?.toIntOrNull()?.let { fontSize ->
+              edit.putInt(app.getString(R.string.preference_textsize_key),
+                          fontSize)
+            }
+            edit.remove("fontsizeChannelList")
+          }
+
+          if (prefs.contains("allowcoloredtext")) {
+            prefs.getBoolean("allowcoloredtext", false).let {
+              edit.putBoolean(app.getString(R.string.preference_colorize_mirc_key),
+                              it)
+            }
+            edit.remove("allowcoloredtext")
+          }
+
+          if (prefs.contains("monospace")) {
+            prefs.getBoolean("monospace", false).let {
+              edit.putBoolean(app.getString(R.string.preference_monospace_key),
+                              it)
+            }
+            edit.remove("monospace")
+          }
+
+          if (prefs.contains("detailed_actions")) {
+            prefs.getBoolean("detailed_actions", false).let {
+              edit.putBoolean(app.getString(R.string.preference_hostmask_actions_key),
+                              it)
+            }
+            edit.remove("detailed_actions")
+          }
+
+          if (prefs.contains("showlag")) {
+            prefs.getBoolean("showlag", false).let {
+              edit.putBoolean(app.getString(R.string.preference_show_lag_key),
+                              it)
+            }
+            edit.remove("showlag")
+          }
+        }
+      )
+    ).migrate(app)
+
+    if (BuildConfig.DEBUG) {
+      StrictMode.setThreadPolicy(
+        StrictMode.ThreadPolicy.Builder()
+          .detectNetwork()
+          .detectCustomSlowCalls()
+          .let {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+              it.detectResourceMismatches()
+            } else {
+              it
+            }
+          }.let {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+              it.detectUnbufferedIo()
+            } else {
+              it
+            }
+          }
+          .penaltyLog()
+          .build()
+      )
+      StrictMode.setVmPolicy(
+        StrictMode.VmPolicy.Builder()
+          .detectLeakedSqlLiteObjects()
+          .detectActivityLeaks()
+          .detectLeakedClosableObjects()
+          .detectLeakedRegistrationObjects()
+          .let {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+              it.detectFileUriExposure()
+            } else {
+              it
+            }
+          }.let {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+              it.detectContentUriWithoutPermission()
+            } else {
+              it
+            }
+          }
+          .penaltyLog()
+          .build()
+      )
+    }
+  }
+
+  override fun onInstallMultidex() {
+    if (BuildConfig.DEBUG) {
+      MultiDex.install(app)
+    }
+  }
+}
diff --git a/app/src/test/java/de/kuschku/quasseldroid/QuasseldroidTest.kt b/app/src/test/java/de/kuschku/quasseldroid/QuasseldroidTest.kt
index 5f0b57dc6..2ecdd1764 100644
--- a/app/src/test/java/de/kuschku/quasseldroid/QuasseldroidTest.kt
+++ b/app/src/test/java/de/kuschku/quasseldroid/QuasseldroidTest.kt
@@ -19,8 +19,8 @@
 
 package de.kuschku.quasseldroid
 
+import de.kuschku.quasseldroid.app.QuasseldroidTestDelegate
+
 class QuasseldroidTest : Quasseldroid() {
-  override fun init() {
-    applicationInjector().inject(this)
-  }
+  override val delegate = QuasseldroidTestDelegate()
 }
diff --git a/app/src/test/java/de/kuschku/quasseldroid/app/QuasseldroidTestDelegate.kt b/app/src/test/java/de/kuschku/quasseldroid/app/QuasseldroidTestDelegate.kt
new file mode 100644
index 000000000..2341e01b8
--- /dev/null
+++ b/app/src/test/java/de/kuschku/quasseldroid/app/QuasseldroidTestDelegate.kt
@@ -0,0 +1,27 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.app
+
+class QuasseldroidTestDelegate : AppDelegate {
+  override fun shouldInit() = true
+  override fun onInstallMultidex() = Unit
+  override fun onInit() = Unit
+  override fun onPostInit() = Unit
+}
-- 
GitLab