From c8457958c0e1fb921f1f0b601f3310a3b5262f4c Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Wed, 8 May 2019 09:31:09 -0700 Subject: [PATCH] I/O '19 updates - Upgrade a ton of deps and use new alpha features - Get rid of billing crap since no one donates and Patreon seems better - Use version codes instead of code names since that's clearer Signed-off-by: Alex Saveau --- app/android-base/build.gradle.kts | 1 - .../supercilex/robotscouter/DonateDialog.kt | 215 ------------------ .../supercilex/robotscouter/HomeActivity.kt | 12 +- .../supercilex/robotscouter/RobotScouter.kt | 2 +- .../src/main/res/layout/dialog_donate.xml | 58 ----- .../src/main/res/menu/home_drawer.xml | 7 +- .../src/main/res/values-fr/strings.xml | 7 - .../src/main/res/values/strings.xml | 9 - buildSrc/src/main/kotlin/Config.kt | 49 ++-- .../exports/ExportNotificationManager.kt | 2 +- .../scouts/ActivityScoutListFragment.kt | 4 +- .../scouts/IntegratedScoutListFragment.kt | 5 +- .../scouts/viewholder/EditTextViewHolder.kt | 2 +- .../feature/settings/SettingsFragment.kt | 4 - .../feature/settings/SettingsViewModel.kt | 25 +- .../viewholder/CounterTemplateViewHolder.kt | 2 +- .../supercilex/robotscouter/core/data/Io.kt | 2 +- .../robotscouter/core/data/Notifications.kt | 8 +- .../robotscouter/core/data/Prefs.kt | 7 +- .../robotscouter/core/data/RemoteConfig.kt | 15 +- .../robotscouter/core/ui/Animations.kt | 2 +- .../robotscouter/core/ui/CardMetric.kt | 4 +- .../robotscouter/core/ui/Selection.kt | 6 +- .../com/supercilex/robotscouter/core/ui/Ui.kt | 2 +- .../supercilex/robotscouter/core/Constants.kt | 2 +- .../viewholder/StopwatchViewHolder.kt | 2 +- .../shared/TemplateSelectorDialog.kt | 8 +- .../robotscouter/shared/client/Auth.kt | 2 +- 28 files changed, 69 insertions(+), 395 deletions(-) delete mode 100644 app/android-base/src/main/java/com/supercilex/robotscouter/DonateDialog.kt delete mode 100644 app/android-base/src/main/res/layout/dialog_donate.xml diff --git a/app/android-base/build.gradle.kts b/app/android-base/build.gradle.kts index 32d571536..2a726df2a 100644 --- a/app/android-base/build.gradle.kts +++ b/app/android-base/build.gradle.kts @@ -82,7 +82,6 @@ dependencies { implementation(Config.Libs.Jetpack.multidex) implementation(Config.Libs.Jetpack.work) implementation(Config.Libs.PlayServices.playCore) - implementation(Config.Libs.Misc.billing) implementation(Config.Libs.Firebase.perf) implementation(Config.Libs.Firebase.invites) diff --git a/app/android-base/src/main/java/com/supercilex/robotscouter/DonateDialog.kt b/app/android-base/src/main/java/com/supercilex/robotscouter/DonateDialog.kt deleted file mode 100644 index 3e05d8ebb..000000000 --- a/app/android-base/src/main/java/com/supercilex/robotscouter/DonateDialog.kt +++ /dev/null @@ -1,215 +0,0 @@ -package com.supercilex.robotscouter - -import android.app.Dialog -import android.content.DialogInterface -import android.os.Bundle -import android.view.View -import android.view.ViewGroup -import android.widget.SeekBar -import androidx.core.view.isVisible -import androidx.fragment.app.FragmentManager -import com.android.billingclient.api.BillingClient -import com.android.billingclient.api.BillingClient.BillingResponse -import com.android.billingclient.api.BillingClientStateListener -import com.android.billingclient.api.BillingFlowParams -import com.android.billingclient.api.Purchase -import com.android.billingclient.api.PurchasesUpdatedListener -import com.google.android.gms.tasks.Continuation -import com.google.android.gms.tasks.Task -import com.google.android.gms.tasks.TaskCompletionSource -import com.google.android.gms.tasks.Tasks -import com.supercilex.robotscouter.core.CrashLogger -import com.supercilex.robotscouter.core.data.uid -import com.supercilex.robotscouter.core.logBreadcrumb -import com.supercilex.robotscouter.core.ui.BottomSheetDialogFragmentBase -import com.supercilex.robotscouter.core.unsafeLazy -import kotlinx.android.synthetic.main.dialog_donate.* -import org.jetbrains.anko.design.longSnackbar -import org.jetbrains.anko.design.snackbar -import org.jetbrains.anko.support.v4.longToast - -internal class DonateDialog : BottomSheetDialogFragmentBase(), View.OnClickListener, - SeekBar.OnSeekBarChangeListener, BillingClientStateListener, PurchasesUpdatedListener { - override val containerView by unsafeLazy { - View.inflate(context, R.layout.dialog_donate, null) as ViewGroup - } - - private val billingClient by unsafeLazy { - BillingClient.newBuilder(requireContext()).setListener(this).build() - } - private val billingClientReadyTask = TaskCompletionSource<@BillingResponse Int>() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - billingClient.startConnection(this) - } - - override fun onDialogCreated(dialog: Dialog, savedInstanceState: Bundle?) { - donate.setOnClickListener(this) - amountSeekBar.setOnSeekBarChangeListener(this) - } - - override fun onShow(dialog: DialogInterface) { - onProgressChanged(amountSeekBar, 0, false) // Init state - } - - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - amount.text = getString(R.string.donate_amount, progress + 1) - - val width = seekBar.width - (seekBar.paddingLeft + seekBar.paddingRight) - val thumbPos = seekBar.paddingLeft + (width * progress / seekBar.max) - val offset = - seekBar.thumbOffset - if (progress == seekBar.max) 2 * seekBar.max else seekBar.max - amount.x = (thumbPos + offset).toFloat() - } - - override fun onDestroy() { - super.onDestroy() - if (billingClient.isReady) billingClient.endConnection() - } - - override fun onClick(v: View) { - if (!billingClient.isReady) { - showError() - return - } - - updateProgress(true) - - val sku = "${amountSeekBar.progress + 1}$SKU_BASE" + - if (monthly.isChecked) "monthly" else "single" - purchaseItem(sku, if (monthly.isChecked) { - BillingClient.SkuType.SUBS - } else { - BillingClient.SkuType.INAPP - }).addOnFailureListener { showError() } - } - - private fun handlePurchaseResponse(response: Task) = response.addOnCompleteListener { - updateProgress(false) - }.addOnSuccessListener { - longToast(R.string.donate_thanks_message) - dismiss() - }.addOnFailureListener { - when ((it as PurchaseException).errorCode) { - BillingResponse.USER_CANCELED -> view.longSnackbar(R.string.donate_cancel_message) - BillingResponse.SERVICE_UNAVAILABLE -> view.longSnackbar(R.string.no_connection) - BillingResponse.ITEM_ALREADY_OWNED, BillingResponse.ERROR -> Unit - else -> { - CrashLogger.onFailure(it) - showError() - } - } - } - - private fun purchaseItem( - sku: String, - @BillingClient.SkuType type: String - ): Task = billingClientReadyTask.task.continueWithTask(Continuation> { - val purchaseStartTask = TaskCompletionSource() - - logBreadcrumb("Starting purchase flow for sku: $sku") - @Suppress("DEPRECATION") - val result = billingClient.launchBillingFlow(activity, BillingFlowParams.newBuilder() - .setSku(sku) - .setType(type) - .setAccountId(uid?.hashCode().toString()) - .build()) - - if (result == BillingResponse.OK) { - purchaseStartTask.setResult(null) - } else if (result == BillingResponse.ITEM_ALREADY_OWNED) { - billingClient.queryPurchaseHistoryAsync(type) { responseCode, purchasesList -> - if (responseCode != BillingResponse.OK) { - val e = PurchaseException( - responseCode, - message = "Purchase fetch failed with code $responseCode and sku $sku" - ) - CrashLogger.onFailure(e) - purchaseStartTask.setException(e) - return@queryPurchaseHistoryAsync - } - - getConsumePurchasesTask(purchasesList) - .continueWithTask { purchaseItem(sku, type) } - .addOnSuccessListener { purchaseStartTask.setResult(null) } - } - } else if (result == BillingResponse.FEATURE_NOT_SUPPORTED) { - view.snackbar(R.string.error_unknown) - } else { - PurchaseException(result, sku).let { - CrashLogger.onFailure(it) - purchaseStartTask.setException(it) - } - } - - return@Continuation purchaseStartTask.task - }) - - private fun getConsumePurchasesTask(purchases: List): Task<*> { - val consumptions = purchases.filter { - it.sku.contains("single") - }.map { - val consumption = TaskCompletionSource() - billingClient.consumeAsync(it.purchaseToken) { resultCode, purchaseToken -> - if (resultCode == BillingResponse.OK || resultCode == BillingResponse.ITEM_NOT_OWNED) { - consumption.setResult(purchaseToken) - } else { - val e = PurchaseException( - resultCode, - message = "Consumption failed with code $resultCode and sku ${it.sku}" - ) - CrashLogger.onFailure(e) - consumption.setException(e) - } - } - consumption.task - } - - return Tasks.whenAllSuccess(consumptions) - } - - private fun updateProgress(isDoingAsyncWork: Boolean) { - if (isDoingAsyncWork) progress.show(Runnable { main.isVisible = false }) - else progress.hide(true, Runnable { main.isVisible = true }) - donate.isEnabled = !isDoingAsyncWork - } - - private fun showError() { - updateProgress(false) - view.longSnackbar(R.string.error_unknown) - } - - override fun onBillingSetupFinished(resultCode: Int) { - billingClientReadyTask.trySetResult(resultCode) - } - - override fun onBillingServiceDisconnected() = billingClient.startConnection(this) - - override fun onPurchasesUpdated(responseCode: Int, purchases: List?) { - updateProgress(true) - if (responseCode == BillingResponse.OK && purchases != null) { - handlePurchaseResponse(getConsumePurchasesTask(purchases).continueWith { responseCode }) - } else { - handlePurchaseResponse(Tasks.forException( - PurchaseException(responseCode, purchases?.map { it.sku }.toString()))) - } - } - - override fun onStartTrackingTouch(seekBar: SeekBar) = Unit - - override fun onStopTrackingTouch(seekBar: SeekBar) = Unit - - private class PurchaseException( - val errorCode: Int, - sku: String = "", - message: String = "Purchase failed with error code $errorCode for sku $sku" - ) : Exception(message) - - companion object { - private const val TAG = "DonateDialog" - private const val SKU_BASE = "_donate_" - - fun show(manager: FragmentManager) = DonateDialog().show(manager, TAG) - } -} diff --git a/app/android-base/src/main/java/com/supercilex/robotscouter/HomeActivity.kt b/app/android-base/src/main/java/com/supercilex/robotscouter/HomeActivity.kt index cfcd0f977..6adcdce14 100644 --- a/app/android-base/src/main/java/com/supercilex/robotscouter/HomeActivity.kt +++ b/app/android-base/src/main/java/com/supercilex/robotscouter/HomeActivity.kt @@ -10,7 +10,6 @@ import android.view.MenuItem import android.view.View import android.view.ViewOutlineProvider import androidx.appcompat.app.ActionBarDrawerToggle -import androidx.appcompat.app.AppCompatDelegate import androidx.core.net.toUri import androidx.core.view.GravityCompat import androidx.core.view.children @@ -164,10 +163,9 @@ internal class HomeActivity : ActivityBase(), NavigationView.OnNavigationItemSel } prefs.asLiveData().observe(this) { bottomNavigation.menu.findItem(R.id.templates).isEnabled = isTemplateEditingAllowed - if (isSignedIn) delegate.setLocalNightMode(AppCompatDelegate.getDefaultNightMode()) } - if (isInTabletMode() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (isInTabletMode() && Build.VERSION.SDK_INT >= 21) { scoutList.outlineProvider = object : ViewOutlineProvider() { private val padding = resources.getDimensionPixelSize(R.dimen.spacing_mini) @@ -250,7 +248,6 @@ internal class HomeActivity : ActivityBase(), NavigationView.OnNavigationItemSel override fun onNavigationItemSelected(item: MenuItem): Boolean { when (val id = item.itemId) { - R.id.action_donate -> DonateDialog.show(supportFragmentManager) R.id.action_donate_patreon -> launchUrl(this, "https://www.patreon.com/SUPERCILEX".toUri()) else -> runIfSignedIn { @@ -320,7 +317,7 @@ internal class HomeActivity : ActivityBase(), NavigationView.OnNavigationItemSel setReorderingAllowed(false) } else if ( transitionView != null && - Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + Build.VERSION.SDK_INT >= 21 ) { addSharedElement(transitionView, "media") fragment.sharedElementEnterTransition = fragmentTransition.clone() @@ -412,7 +409,8 @@ internal class HomeActivity : ActivityBase(), NavigationView.OnNavigationItemSel bottomNavigation.selectedItemId = R.id.templates } } - containsKey(DONATE_EXTRA) -> DonateDialog.show(supportFragmentManager) + containsKey(DONATE_EXTRA) -> + launchUrl(this@HomeActivity, "https://www.patreon.com/SUPERCILEX".toUri()) containsKey(UPDATE_EXTRA) -> showStoreListing() } } @@ -458,7 +456,7 @@ internal class HomeActivity : ActivityBase(), NavigationView.OnNavigationItemSel } init { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (Build.VERSION.SDK_INT >= 21) { GlobalScope.launch { fragmentTransition } } } diff --git a/app/android-base/src/main/java/com/supercilex/robotscouter/RobotScouter.kt b/app/android-base/src/main/java/com/supercilex/robotscouter/RobotScouter.kt index cb3a86ae7..be5f788d5 100644 --- a/app/android-base/src/main/java/com/supercilex/robotscouter/RobotScouter.kt +++ b/app/android-base/src/main/java/com/supercilex/robotscouter/RobotScouter.kt @@ -54,7 +54,7 @@ internal class RobotScouter : MultiDexApplication(), Configuration.Provider { if (BuildConfig.DEBUG) { // Purposefully put this after initialization since Google is terrible with disk I/O. val vmBuilder = StrictMode.VmPolicy.Builder().detectAll() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (Build.VERSION.SDK_INT >= 28) { vmBuilder.penaltyListener(mainExecutor, StrictMode.OnVmViolationListener { if ( it !is UntaggedSocketViolation diff --git a/app/android-base/src/main/res/layout/dialog_donate.xml b/app/android-base/src/main/res/layout/dialog_donate.xml deleted file mode 100644 index 3882f7edf..000000000 --- a/app/android-base/src/main/res/layout/dialog_donate.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/android-base/src/main/res/menu/home_drawer.xml b/app/android-base/src/main/res/menu/home_drawer.xml index b6df9298f..a78a4723a 100644 --- a/app/android-base/src/main/res/menu/home_drawer.xml +++ b/app/android-base/src/main/res/menu/home_drawer.xml @@ -20,14 +20,9 @@ - - Installation du module… - - Faites un don - %d€ - Faire un don automatiquement chaque mois - Êtes-vous certains de ne pas vouloir faire de don ? Nous apprécions toute contribution, peu importe la taille ! - Merci beaucoup ! Toute aide possible est grandement appréciée ! - Équipes importées avec succès Erreur : analyse de l\'URL impossible diff --git a/app/android-base/src/main/res/values/strings.xml b/app/android-base/src/main/res/values/strings.xml index b1e629ee9..d391e9510 100644 --- a/app/android-base/src/main/res/values/strings.xml +++ b/app/android-base/src/main/res/values/strings.xml @@ -17,16 +17,7 @@ Try again Installing module… - - - Donate Support development - $%d - Automatically donate each month - - Are you sure you don’t want to donate? We appreciate any donation no matter how small! - - Thank you!!! Any and all help is greatly appreciated! Teams successfully imported diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt index f3effab99..05d4149e7 100644 --- a/buildSrc/src/main/kotlin/Config.kt +++ b/buildSrc/src/main/kotlin/Config.kt @@ -43,7 +43,7 @@ object Config { val kotlin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" val google = "com.google.gms:google-services:4.2.0" - val firebase = "com.google.firebase:perf-plugin:1.2.0" + val firebase = "com.google.firebase:perf-plugin:1.2.1" val fabric = "io.fabric.tools:gradle:1.28.1" val ktlint = "com.pinterest:ktlint:0.32.0" @@ -78,23 +78,23 @@ object Config { } object Jetpack { - private const val lifecycleVersion = "2.1.0-alpha04" + private const val lifecycleVersion = "2.2.0-alpha01" - val core = "androidx.core:core-ktx:1.1.0-alpha05" + val core = "androidx.core:core-ktx:1.1.0-beta01" val multidex = "androidx.multidex:multidex:2.0.1" - val appCompat = "androidx.appcompat:appcompat:1.1.0-alpha04" - val fragment = "androidx.fragment:fragment-ktx:1.1.0-alpha07" - val rv = "androidx.recyclerview:recyclerview:1.1.0-alpha04" - val rvSelection = "androidx.recyclerview:recyclerview-selection:1.1.0-alpha01" + val appCompat = "androidx.appcompat:appcompat:1.1.0-alpha05" + val fragment = "androidx.fragment:fragment-ktx:1.1.0-alpha08" + val rv = "androidx.recyclerview:recyclerview:1.1.0-alpha05" + val rvSelection = "androidx.recyclerview:recyclerview-selection:1.1.0-alpha05" val constraint = "androidx.constraintlayout:constraintlayout:1.1.3" val cardView = "androidx.cardview:cardview:1.0.0" val palette = "androidx.palette:palette-ktx:1.0.0" val emoji = "androidx.emoji:emoji-appcompat:1.0.0" val browser = "androidx.browser:browser:1.0.0" - val pref = "androidx.preference:preference:1.1.0-alpha04" - val prefKtx = "androidx.preference:preference-ktx:1.1.0-alpha04" + val pref = "androidx.preference:preference:1.1.0-alpha05" + val prefKtx = "androidx.preference:preference-ktx:1.1.0-alpha05" - val material = "com.google.android.material:material:1.1.0-alpha05" + val material = "com.google.android.material:material:1.1.0-alpha06" val common = "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion" val extensions = "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion" @@ -106,25 +106,25 @@ object Config { } object Firebase { - val core = "com.google.firebase:firebase-analytics:16.4.0" - val auth = "com.google.firebase:firebase-auth:16.2.1" - val firestore = "com.google.firebase:firebase-firestore:18.2.0" - val firestoreKtx = "com.google.firebase:firebase-firestore-ktx:18.2.0" - val functions = "com.google.firebase:firebase-functions:16.3.0" - val storage = "com.google.firebase:firebase-storage:16.1.0" - val config = "com.google.firebase:firebase-config:16.5.0" - val indexing = "com.google.firebase:firebase-appindexing:17.1.0" - val messaging = "com.google.firebase:firebase-messaging:17.6.0" - val invites = "com.google.firebase:firebase-invites:16.1.1" - val perf = "com.google.firebase:firebase-perf:16.2.5" - - val crashlytics = "com.crashlytics.sdk.android:crashlytics:2.9.9" + val core = "com.google.firebase:firebase-analytics:16.5.0" + val auth = "com.google.firebase:firebase-auth:17.0.0" + val firestore = "com.google.firebase:firebase-firestore:19.0.0" + val firestoreKtx = "com.google.firebase:firebase-firestore-ktx:19.0.0" + val functions = "com.google.firebase:firebase-functions:17.0.0" + val storage = "com.google.firebase:firebase-storage:17.0.0" + val config = "com.google.firebase:firebase-config:17.0.0" + val indexing = "com.google.firebase:firebase-appindexing:18.0.0" + val messaging = "com.google.firebase:firebase-messaging:18.0.0" + val invites = "com.google.firebase:firebase-invites:17.0.0" + val perf = "com.google.firebase:firebase-perf:17.0.0" + + val crashlytics = "com.crashlytics.sdk.android:crashlytics:2.10.0" } object PlayServices { val auth = "com.google.android.gms:play-services-auth:16.0.1" val nearby = "com.google.android.gms:play-services-nearby:16.0.0" - val playCore = "com.google.android.play:core:1.4.1" + val playCore = "com.google.android.play:core:1.5.0" } object FirebaseUi { @@ -158,7 +158,6 @@ object Config { val snap = "com.github.rubensousa:gravitysnaphelper:2.0" val permissions = "pub.devrel:easypermissions:3.0.0" val mttp = "uk.co.samuelwall:material-tap-target-prompt:2.14.0" - val billing = "com.android.billingclient:billing:1.2.2" val licenses = "net.yslibrary.licenseadapter:licenseadapter:2.2.2" } } diff --git a/feature/exports/src/main/java/com/supercilex/robotscouter/feature/exports/ExportNotificationManager.kt b/feature/exports/src/main/java/com/supercilex/robotscouter/feature/exports/ExportNotificationManager.kt index 4d06086c2..dacfaebc4 100644 --- a/feature/exports/src/main/java/com/supercilex/robotscouter/feature/exports/ExportNotificationManager.kt +++ b/feature/exports/src/main/java/com/supercilex/robotscouter/feature/exports/ExportNotificationManager.kt @@ -165,7 +165,7 @@ internal class ExportNotificationManager(private val service: ExportService) { notificationFilter.notify( id, notification.setGroup(permanentGroupId.toString()).build(), true) - if (pendingTaskCount == nTemplates && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (pendingTaskCount == nTemplates && Build.VERSION.SDK_INT >= 24) { showExportedPermanentNotification() } diff --git a/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/ActivityScoutListFragment.kt b/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/ActivityScoutListFragment.kt index fe22869bc..ac383c76a 100644 --- a/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/ActivityScoutListFragment.kt +++ b/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/ActivityScoutListFragment.kt @@ -64,10 +64,10 @@ internal class ActivityScoutListFragment : ScoutListFragmentBase(), FirebaseAuth } private fun setTaskDescription(@ColorInt colorPrimary: Int) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (Build.VERSION.SDK_INT >= 28) { activity?.setTaskDescription( ActivityManager.TaskDescription(team.toString(), 0, colorPrimary)) - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + } else if (Build.VERSION.SDK_INT >= 21) { @Suppress("DEPRECATION") activity?.setTaskDescription( ActivityManager.TaskDescription(team.toString(), null, colorPrimary)) diff --git a/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/IntegratedScoutListFragment.kt b/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/IntegratedScoutListFragment.kt index d9d749bf8..6e4ace0a7 100644 --- a/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/IntegratedScoutListFragment.kt +++ b/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/IntegratedScoutListFragment.kt @@ -25,6 +25,7 @@ import com.supercilex.robotscouter.core.ui.isInTabletMode import com.supercilex.robotscouter.core.ui.transitionAnimationDuration import com.supercilex.robotscouter.core.unsafeLazy import org.jetbrains.anko.find +import java.util.concurrent.TimeUnit import com.supercilex.robotscouter.R as RC @Bridge @@ -40,7 +41,7 @@ internal class IntegratedScoutListFragment : ScoutListFragmentBase() { removeFragment() (activity as TeamSelectionListener).onTeamSelected(bundle) } else if (sharedElementEnterTransition != null) { - postponeEnterTransition() + postponeEnterTransition(2, TimeUnit.SECONDS) } } @@ -66,7 +67,7 @@ internal class IntegratedScoutListFragment : ScoutListFragmentBase() { override fun startPostponedEnterTransition() { super.startPostponedEnterTransition() appBar.postDelayed(transitionAnimationDuration) { - appBar.setExpanded(false) + if (isAdded) appBar.setExpanded(false) } } diff --git a/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/viewholder/EditTextViewHolder.kt b/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/viewholder/EditTextViewHolder.kt index e01d6d930..c6d1f2094 100644 --- a/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/viewholder/EditTextViewHolder.kt +++ b/feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/viewholder/EditTextViewHolder.kt @@ -29,7 +29,7 @@ internal class EditTextViewHolder( textLayout.isHintAnimationEnabled = true if ( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && + Build.VERSION.SDK_INT >= 26 && metric.name.contains(View.AUTOFILL_HINT_NAME, true) ) { itemView.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_AUTO diff --git a/feature/settings/src/main/java/com/supercilex/robotscouter/feature/settings/SettingsFragment.kt b/feature/settings/src/main/java/com/supercilex/robotscouter/feature/settings/SettingsFragment.kt index a4cb1395e..53f02890c 100644 --- a/feature/settings/src/main/java/com/supercilex/robotscouter/feature/settings/SettingsFragment.kt +++ b/feature/settings/src/main/java/com/supercilex/robotscouter/feature/settings/SettingsFragment.kt @@ -8,7 +8,6 @@ import android.os.Bundle import android.text.method.LinkMovementMethod import android.util.TypedValue import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.getSystemService import androidx.core.graphics.drawable.DrawableCompat @@ -45,9 +44,6 @@ internal class SettingsFragment : PreferenceFragmentBase(), override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) settingsModel.init() - settingsModel.nightModeChanged.observe(this) { - (requireActivity() as AppCompatActivity).delegate.localNightMode = it - } settingsModel.signOutListener.observe(this) { if (it == null) { FirebaseAuth.getInstance().signInAnonymously() diff --git a/feature/settings/src/main/java/com/supercilex/robotscouter/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/java/com/supercilex/robotscouter/feature/settings/SettingsViewModel.kt index 9702d7a42..5bfd2fc3f 100644 --- a/feature/settings/src/main/java/com/supercilex/robotscouter/feature/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/java/com/supercilex/robotscouter/feature/settings/SettingsViewModel.kt @@ -1,6 +1,5 @@ package com.supercilex.robotscouter.feature.settings -import androidx.appcompat.app.AppCompatDelegate import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle @@ -8,31 +7,15 @@ import androidx.lifecycle.viewModelScope import com.bumptech.glide.Glide import com.supercilex.robotscouter.core.CrashLogger import com.supercilex.robotscouter.core.RobotScouter -import com.supercilex.robotscouter.core.data.ChangeEventListenerBase import com.supercilex.robotscouter.core.data.SimpleViewModelBase import com.supercilex.robotscouter.core.data.cleanup -import com.supercilex.robotscouter.core.data.isSignedIn -import com.supercilex.robotscouter.core.data.prefs import com.supercilex.robotscouter.shared.client.idpSignOut import kotlinx.coroutines.launch -internal class SettingsViewModel(state: SavedStateHandle) : SimpleViewModelBase(state), - ChangeEventListenerBase { - private val _nightModeChanged = MutableLiveData() - val nightModeChanged: LiveData = _nightModeChanged - +internal class SettingsViewModel(state: SavedStateHandle) : SimpleViewModelBase(state) { private val _signOutListener = MutableLiveData() val signOutListener: LiveData = _signOutListener - override fun onCreate() { - prefs.keepAlive = true - prefs.addChangeEventListener(this) - } - - override fun onDataChanged() { - if (isSignedIn) _nightModeChanged.value = AppCompatDelegate.getDefaultNightMode() - } - fun signOut() { cleanup() Glide.get(RobotScouter).clearMemory() @@ -47,10 +30,4 @@ internal class SettingsViewModel(state: SavedStateHandle) : SimpleViewModelBase( } } } - - override fun onCleared() { - super.onCleared() - prefs.keepAlive = false - prefs.removeChangeEventListener(this) - } } diff --git a/feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/viewholder/CounterTemplateViewHolder.kt b/feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/viewholder/CounterTemplateViewHolder.kt index 7a750a49c..595196fdf 100644 --- a/feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/viewholder/CounterTemplateViewHolder.kt +++ b/feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/viewholder/CounterTemplateViewHolder.kt @@ -31,7 +31,7 @@ internal class CounterTemplateViewHolder(itemView: View) : CounterViewHolder(ite itemView.removeView(unit) itemView.addView(unit, itemView.childCount - 1) itemView.find(RC.id.countContainer).apply { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (Build.VERSION.SDK_INT >= 17) { updatePaddingRelative(end = 0) } else { updatePadding(right = 0) diff --git a/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Io.kt b/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Io.kt index e30b7b156..a021effb2 100644 --- a/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Io.kt +++ b/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Io.kt @@ -13,7 +13,7 @@ const val MIME_TYPE_ANY = "*/*" val ioPerms = listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) private val exports = Environment.getExternalStoragePublicDirectory( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (Build.VERSION.SDK_INT >= 19) { Environment.DIRECTORY_DOCUMENTS } else { "Documents" diff --git a/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Notifications.kt b/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Notifications.kt index ff706c931..ba09882bb 100644 --- a/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Notifications.kt +++ b/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Notifications.kt @@ -48,7 +48,7 @@ fun initNotifications() { } ) - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return + if (Build.VERSION.SDK_INT < 26) return notificationManager.createNotificationChannelGroups( listOf(NotificationChannelGroup( @@ -61,7 +61,7 @@ fun initNotifications() { ) } -@RequiresApi(Build.VERSION_CODES.O) +@RequiresApi(26) private fun getExportChannel(): NotificationChannel = NotificationChannel( EXPORT_CHANNEL, RobotScouter.getString(R.string.export_channel_title), @@ -74,7 +74,7 @@ private fun getExportChannel(): NotificationChannel = NotificationChannel( enableLights(true) } -@RequiresApi(Build.VERSION_CODES.O) +@RequiresApi(26) private fun getExportInProgressChannel(): NotificationChannel = NotificationChannel( EXPORT_IN_PROGRESS_CHANNEL, RobotScouter.getString(R.string.export_progress_channel_title), @@ -189,7 +189,7 @@ class NotificationIntentForwarder : Activity() { systemNotificationManager.apply { val notificationId = intent.getIntExtra(KEY_NOTIFICATION_ID, -1) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (Build.VERSION.SDK_INT >= 23) { // Cancel group notification if there will only be one real notification left activeNotifications.singleOrNull { it.id == notificationId diff --git a/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Prefs.kt b/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Prefs.kt index f3a0d737c..69514ad67 100644 --- a/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Prefs.kt +++ b/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Prefs.kt @@ -2,6 +2,7 @@ package com.supercilex.robotscouter.core.data import android.content.Context import android.content.SharedPreferences +import android.os.Build import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.edit import androidx.preference.PreferenceDataStore @@ -79,7 +80,11 @@ var defaultTemplateId: String @get:AppCompatDelegate.NightMode val nightMode: Int get() = when (val mode = prefStore.getString(FIRESTORE_PREF_NIGHT_MODE, "auto")) { - "auto" -> AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY + "auto" -> if (Build.VERSION.SDK_INT >= 29) { + AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + } else { + AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY + } "yes" -> AppCompatDelegate.MODE_NIGHT_YES "no" -> AppCompatDelegate.MODE_NIGHT_NO else -> error("Unknown night mode value: $mode") diff --git a/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/RemoteConfig.kt b/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/RemoteConfig.kt index c44cb562e..4090f5687 100644 --- a/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/RemoteConfig.kt +++ b/library/core-data/src/main/java/com/supercilex/robotscouter/core/data/RemoteConfig.kt @@ -6,7 +6,6 @@ import com.supercilex.robotscouter.core.InvocationMarker import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.tasks.await -import java.util.concurrent.TimeUnit // Mirrored in remote_config_defaults.xml private const val KEY_MINIMUM_APP_VERSION = "minimum_app_version" @@ -28,18 +27,16 @@ val enableAutoScout fun initRemoteConfig() { FirebaseRemoteConfig.getInstance().apply { setDefaults(R.xml.remote_config_defaults) - setConfigSettings(FirebaseRemoteConfigSettings.Builder() - .setDeveloperModeEnabled(BuildConfig.DEBUG) - .build()) + if (BuildConfig.DEBUG) { + setConfigSettingsAsync(FirebaseRemoteConfigSettings.Builder() + .setMinimumFetchIntervalInSeconds(0) + .build()) + } GlobalScope.launch { try { activate().await() - fetch(if (info.configSettings.isDeveloperModeEnabled) { - 0 - } else { - TimeUnit.HOURS.toSeconds(12) - }).await() + fetch().await() } catch (e: Exception) { throw InvocationMarker(e) } diff --git a/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Animations.kt b/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Animations.kt index 56b24d154..a785345b4 100644 --- a/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Animations.kt +++ b/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Animations.kt @@ -64,7 +64,7 @@ fun View.animateCircularReveal( centerY: Int, radius: Float ): Animator? = getRevealAnimation(visible) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + if (Build.VERSION.SDK_INT < 21) { isVisible = visible return@getRevealAnimation null } diff --git a/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/CardMetric.kt b/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/CardMetric.kt index d9cdf07bf..75db2cf89 100644 --- a/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/CardMetric.kt +++ b/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/CardMetric.kt @@ -53,7 +53,7 @@ class CardMetricHelper(private val view: View) { private val rectF = AndroidRectF() private val provider by unsafeLazy { - @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @TargetApi(21) object : ViewOutlineProvider() { override fun getOutline(view: View, outline: Outline) { outline.setRoundRect(0, 0, view.width, view.height, cornerRadius) @@ -75,7 +75,7 @@ class CardMetricHelper(private val view: View) { } fun onSizeChanged() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (Build.VERSION.SDK_INT >= 21) { view.outlineProvider = provider } } diff --git a/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Selection.kt b/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Selection.kt index a648ffd0d..4fcab1b75 100644 --- a/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Selection.kt +++ b/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Selection.kt @@ -177,13 +177,13 @@ abstract class ToolbarMenuHelperBase( tracker: SelectionTracker ) : MenuHelperBase(activity, tracker) { init { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (Build.VERSION.SDK_INT >= 21) { activity.window.statusBarColor = colorPrimaryDark } } override fun shouldUpdateStatusBarColor(new: Int): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return if (Build.VERSION.SDK_INT >= 21) { activity.window.statusBarColor != ContextCompat.getColor(activity, new) } else { false @@ -191,7 +191,7 @@ abstract class ToolbarMenuHelperBase( } override fun updateStatusBarColor(value: Int) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (Build.VERSION.SDK_INT >= 21) { activity.window.statusBarColor = value } } diff --git a/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Ui.kt b/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Ui.kt index e4e13b984..6e46c4f69 100644 --- a/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Ui.kt +++ b/library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Ui.kt @@ -32,7 +32,7 @@ fun Context.isInTabletMode(): Boolean { fun View.setOnLongClickListenerCompat(listener: View.OnLongClickListener) { setOnLongClickListener(listener) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (Build.VERSION.SDK_INT >= 23) { setOnContextClickListener { listener.onLongClick(this) } } } diff --git a/library/core/src/main/java/com/supercilex/robotscouter/core/Constants.kt b/library/core/src/main/java/com/supercilex/robotscouter/core/Constants.kt index 4185c812a..1829ab584 100644 --- a/library/core/src/main/java/com/supercilex/robotscouter/core/Constants.kt +++ b/library/core/src/main/java/com/supercilex/robotscouter/core/Constants.kt @@ -19,7 +19,7 @@ val fullVersionName: String by lazy { val fullVersionCode by lazy { // See fullVersionName RobotScouter.packageManager.getPackageInfo(RobotScouter.packageName, 0).run { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (Build.VERSION.SDK_INT >= 28) { longVersionCode } else { @Suppress("DEPRECATION") diff --git a/library/shared-scouting/src/main/java/com/supercilex/robotscouter/shared/scouting/viewholder/StopwatchViewHolder.kt b/library/shared-scouting/src/main/java/com/supercilex/robotscouter/shared/scouting/viewholder/StopwatchViewHolder.kt index 96e5cb427..daf6822f8 100644 --- a/library/shared-scouting/src/main/java/com/supercilex/robotscouter/shared/scouting/viewholder/StopwatchViewHolder.kt +++ b/library/shared-scouting/src/main/java/com/supercilex/robotscouter/shared/scouting/viewholder/StopwatchViewHolder.kt @@ -194,7 +194,7 @@ open class StopwatchViewHolder( // There's a bug pre-L where changing the view state doesn't update the vector drawable. // Because of that, calling View#setActivated(isRunning) doesn't update the background // color and we end up with unreadable text. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return + if (Build.VERSION.SDK_INT < 21) return stopwatch.isActivated = isRunning diff --git a/library/shared/src/main/java/com/supercilex/robotscouter/shared/TemplateSelectorDialog.kt b/library/shared/src/main/java/com/supercilex/robotscouter/shared/TemplateSelectorDialog.kt index 3f0375ec2..c5c50af71 100644 --- a/library/shared/src/main/java/com/supercilex/robotscouter/shared/TemplateSelectorDialog.kt +++ b/library/shared/src/main/java/com/supercilex/robotscouter/shared/TemplateSelectorDialog.kt @@ -3,7 +3,6 @@ package com.supercilex.robotscouter.shared import android.app.Dialog import android.graphics.Canvas import android.graphics.Rect -import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle import android.view.LayoutInflater @@ -91,10 +90,6 @@ abstract class TemplateSelectorDialog : BottomSheetDialogFragmentBase() { context, DividerItemDecoration.VERTICAL ) { - private val divider = DividerItemDecoration::class.java - .getDeclaredField("mDivider") - .apply { isAccessible = true } - .get(this) as Drawable private val bounds = Rect() override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { @@ -104,7 +99,7 @@ abstract class TemplateSelectorDialog : BottomSheetDialogFragmentBase() { val left: Int val right: Int - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && parent.clipToPadding) { + if (Build.VERSION.SDK_INT >= 21 && parent.clipToPadding) { left = parent.paddingLeft right = parent.width - parent.paddingRight c.clipRect(left, parent.paddingTop, right, parent.height - parent.paddingBottom) @@ -118,6 +113,7 @@ abstract class TemplateSelectorDialog : BottomSheetDialogFragmentBase() { parent.getChildAt(1 - (templatesView.layoutManager as LinearLayoutManager) .findFirstVisibleItemPosition()) ?: return parent.getDecoratedBoundsWithMargins(child, bounds) + val divider = checkNotNull(drawable) val bottom = bounds.bottom + child.translationY.roundToInt() val top = bottom - divider.intrinsicHeight divider.setBounds(left, top, right, bottom) diff --git a/library/shared/src/main/java/com/supercilex/robotscouter/shared/client/Auth.kt b/library/shared/src/main/java/com/supercilex/robotscouter/shared/client/Auth.kt index 671a6d6d5..b7efed013 100644 --- a/library/shared/src/main/java/com/supercilex/robotscouter/shared/client/Auth.kt +++ b/library/shared/src/main/java/com/supercilex/robotscouter/shared/client/Auth.kt @@ -33,7 +33,7 @@ private val allProviders: List = listOf( private val unlinkedProviders: List get() { - val existingProviders = user?.providers.orEmpty() + val existingProviders = user?.providerData.orEmpty().map { it.providerId } return allProviders.filterNot { existingProviders.contains(it.providerId) } }