Beast mode banner

pull/1826/head
Alex Baker 2 years ago
parent 1de4b220c3
commit 46a6996982

@ -5,6 +5,7 @@ import androidx.annotation.StringRes
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import at.bitfire.cert4android.CustomCertManager.Companion.resetCertificates
import com.todoroo.andlib.utility.DateUtilities.now
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.tasks.R
@ -12,13 +13,17 @@ import org.tasks.billing.BillingClient
import org.tasks.billing.Inventory
import org.tasks.extensions.Context.toast
import org.tasks.injection.InjectingPreferenceFragment
import org.tasks.preferences.Preferences
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.math.min
@AndroidEntryPoint
class Debug : InjectingPreferenceFragment() {
@Inject lateinit var inventory: Inventory
@Inject lateinit var billingClient: BillingClient
@Inject lateinit var preferences: Preferences
override fun getPreferenceXml() = R.xml.preferences_debug
@ -54,6 +59,15 @@ class Debug : InjectingPreferenceFragment() {
findPreference(R.string.debug_crash_app).setOnPreferenceClickListener {
throw RuntimeException("Crashed app from debug preferences")
}
findPreference(R.string.debug_clear_hints).setOnPreferenceClickListener {
preferences.installDate =
min(preferences.installDate, now() - TimeUnit.DAYS.toMillis(14))
preferences.lastSubscribeRequest = 0L
preferences.lastReviewRequest = 0L
preferences.shownBeastModeHint = false
true
}
}
private fun setupIap(@StringRes prefId: Int, sku: String) {

@ -14,5 +14,6 @@
<string name="debug_crash_app">Crash app now</string>
<string name="debug_main_queries">Crash on violation</string>
<string name="debug_force_restart">Restart app</string>
<string name="debug_clear_hints">Clear hints</string>
<string name="google_oauth_scheme">com.googleusercontent.apps.1006257750459-vf4mvft1b3rfda8b4c4bl4k4418abqlf</string>
</resources>

@ -49,4 +49,8 @@
<Preference
android:key="@string/debug_tasker"/>
<Preference
android:key="@string/debug_clear_hints"
android:title="@string/debug_clear_hints" />
</PreferenceScreen>

@ -7,6 +7,7 @@ package com.todoroo.astrid.activity
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Paint
import android.net.Uri
import android.os.Bundle
@ -16,7 +17,14 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.Toolbar
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
@ -25,6 +33,7 @@ import androidx.lifecycle.lifecycleScope
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.AppBarLayout.Behavior.DragCallback
import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener
import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter
@ -43,6 +52,7 @@ import kotlinx.coroutines.withContext
import org.tasks.R
import org.tasks.Strings.isNullOrEmpty
import org.tasks.analytics.Firebase
import org.tasks.compose.BeastModeBanner
import org.tasks.data.Alarm
import org.tasks.data.Location
import org.tasks.data.TagData
@ -225,6 +235,7 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
.launchIn(viewLifecycleOwner.lifecycleScope)
}
@OptIn(ExperimentalAnimationApi::class)
private suspend fun process(event: TaskEditEvent) {
when (event) {
is TaskEditEvent.Discard ->
@ -234,6 +245,37 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
}
private val beastMode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val transaction = childFragmentManager.beginTransaction()
taskEditControlSetFragmentManager.getOrCreateFragments(childFragmentManager).forEach {
transaction.remove(it)
}
transaction.commit()
activity?.recreate()
}
@OptIn(ExperimentalAnimationApi::class)
private fun showBeastModeHint() {
binding.banner.setContent {
var visible by rememberSaveable { mutableStateOf(true) }
val context = LocalContext.current
MdcTheme {
BeastModeBanner(
visible,
showSettings = {
visible = false
preferences.shownBeastModeHint = true
beastMode.launch(Intent(context, BeastModePreferences::class.java))
},
dismiss = {
visible = false
preferences.shownBeastModeHint = true
}
)
}
}
}
override fun onResume() {
super.onResume()
@ -242,6 +284,9 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(binding.title, InputMethodManager.SHOW_IMPLICIT)
}
if (!preferences.shownBeastModeHint) {
showBeastModeHint()
}
}
override fun onMenuItemClick(item: MenuItem): Boolean {

@ -24,8 +24,10 @@ import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.ShareCompat
import androidx.core.view.forEach
@ -89,7 +91,7 @@ import org.tasks.activities.TagSettingsActivity
import org.tasks.analytics.Firebase
import org.tasks.billing.PurchaseActivity
import org.tasks.caldav.BaseCaldavCalendarSettingsActivity
import org.tasks.compose.AnimatedBanner
import org.tasks.compose.SubscriptionNagBanner
import org.tasks.data.CaldavDao
import org.tasks.data.TagDataDao
import org.tasks.data.TaskContainer
@ -186,17 +188,17 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
?.show()
is TaskListEvent.BegForSubscription -> {
binding.banner.setContent {
val showBanner = rememberSaveable { mutableStateOf(true) }
var showBanner by rememberSaveable { mutableStateOf(true) }
MdcTheme {
AnimatedBanner(
isVisible = showBanner,
dismiss = {
preferences.lastSubscribeRequest = now()
showBanner.value = false
},
SubscriptionNagBanner(
visible = showBanner,
subscribe = {
showBanner = false
purchase()
showBanner.value = false
},
dismiss = {
showBanner = false
preferences.lastSubscribeRequest = now()
},
)
}

@ -1,5 +1,6 @@
package org.tasks.compose
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.expandVertically
@ -16,51 +17,60 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
import com.google.android.material.composethemeadapter.MdcTheme
import org.tasks.R
import org.tasks.Tasks.Companion.IS_GENERIC
import org.tasks.Tasks
@ExperimentalAnimationApi
@Composable
fun AnimatedBanner(
isVisible: MutableState<Boolean>,
dismiss: () -> Unit,
subscribe: () -> Unit,
visible: Boolean,
content: @Composable () -> Unit,
buttons: @Composable () -> Unit,
) {
var show by rememberSaveable { mutableStateOf(false) }
if (isVisible.value) {
LaunchedEffect(key1 = isVisible, block = {
delay(500)
show = true
})
} else {
show = false
}
AnimatedVisibility(
visible = show,
enter = expandVertically(
expandFrom = Alignment.Top
),
visible = visible,
enter = expandVertically(),
exit = shrinkVertically(),
) {
Column(
modifier = Modifier
.fillMaxWidth(),
) {
Divider()
Spacer(modifier = Modifier.height(16.dp))
content()
Row(
modifier = Modifier
.padding(vertical = 8.dp, horizontal = 16.dp)
.align(Alignment.End),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
buttons()
}
Divider()
}
}
}
@ExperimentalAnimationApi
@Composable
fun SubscriptionNagBanner(
visible: Boolean,
subscribe: () -> Unit,
dismiss: () -> Unit,
) {
AnimatedBanner(
visible = visible,
content = {
Text(
text = stringResource(
id = if (IS_GENERIC) {
id = if (Tasks.IS_GENERIC) {
R.string.enjoying_tasks
} else {
R.string.tasks_needs_your_support
@ -72,7 +82,7 @@ fun AnimatedBanner(
Spacer(modifier = Modifier.height(4.dp))
Text(
text = stringResource(
id = if (IS_GENERIC) {
id = if (Tasks.IS_GENERIC) {
R.string.tasks_needs_your_support
} else {
R.string.support_development_subscribe
@ -81,28 +91,69 @@ fun AnimatedBanner(
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(horizontal = 16.dp),
)
Row(
modifier = Modifier
.padding(vertical = 8.dp, horizontal = 16.dp)
.align(Alignment.End),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
TextButton(onClick = dismiss) {
Text(text = stringResource(id = R.string.dismiss))
}
TextButton(onClick = subscribe) {
Text(
text = stringResource(
id = if (IS_GENERIC) {
R.string.TLA_menu_donate
} else {
R.string.button_subscribe
}
)
)
},
buttons = {
TextButton(onClick = subscribe) {
Text(text = stringResource(id = R.string.dismiss))
}
TextButton(onClick = dismiss) {
val res = if (Tasks.IS_GENERIC) {
R.string.TLA_menu_donate
} else {
R.string.button_subscribe
}
Text(text = stringResource(id = res))
}
Divider()
}
}
}
)
}
@ExperimentalAnimationApi
@Composable
fun BeastModeBanner(
visible: Boolean,
showSettings: () -> Unit,
dismiss: () -> Unit,
) {
AnimatedBanner(
visible = visible,
content = {
Text(
text = stringResource(id = R.string.hint_customize_edit_title),
style = MaterialTheme.typography.body1,
modifier = Modifier.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = stringResource(id = R.string.hint_customize_edit_body),
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(horizontal = 16.dp),
)
},
buttons = {
TextButton(onClick = dismiss) {
Text(text = stringResource(id = R.string.dismiss))
}
TextButton(onClick = showSettings) {
Text(text = stringResource(id = R.string.TLA_menu_settings))
}
}
)
}
@ExperimentalAnimationApi
@Preview(showBackground = true)
@Preview(showBackground = true, backgroundColor = 0x202124, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun BeastModePreview() = MdcTheme {
BeastModeBanner(visible = true, showSettings = {}, dismiss = {})
}
@ExperimentalAnimationApi
@Preview(showBackground = true)
@Preview(showBackground = true, backgroundColor = 0x202124, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun SubscriptionNagPreview() = MdcTheme {
SubscriptionNagBanner(visible = true, subscribe = {}, dismiss = {})
}

@ -540,6 +540,10 @@ class Preferences @JvmOverloads constructor(
get() = getLong(R.string.p_last_subscribe_request, 0L)
set(value) = setLong(R.string.p_last_subscribe_request, value)
var shownBeastModeHint: Boolean
get() = getBoolean(R.string.p_shown_beast_mode_hint, false)
set(value) = setBoolean(R.string.p_shown_beast_mode_hint, value)
companion object {
private const val PREF_SORT_SORT = "sort_sort" // $NON-NLS-1$

@ -66,6 +66,11 @@
android:orientation="vertical"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/banner"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.core.widget.NestedScrollView
android:scrollbarStyle="outsideOverlay"
android:layout_width="fill_parent"

@ -439,4 +439,5 @@
<string name="p_app_bar_collapse">app_bar_collapse</string>
<string name="p_completed_tasks_at_bottom">completed_tasks_at_bottom</string>
<string name="p_completed_tasks_sort">completed_tasks_sort</string>
<string name="p_shown_beast_mode_hint">shown_beast_mode_hint</string>
</resources>

@ -735,4 +735,6 @@ File %1$s contained %2$s.\n\n
<string name="caldav_server_type">Server type</string>
<string name="filter_snoozed">Snoozed</string>
<string name="dismiss">Dismiss</string>
<string name="hint_customize_edit_title">Too much information?</string>
<string name="hint_customize_edit_body">You can customize this screen by rearranging or removing fields</string>
</resources>

Loading…
Cancel
Save