New Tasks account settings preference

pull/1228/head
Alex Baker 4 years ago
parent 9d37a20c95
commit 6fe02f6a99

@ -385,10 +385,6 @@
android:name=".opentasks.OpenTaskAccountSettingsActivity"
android:theme="@style/Tasks" />
<activity
android:name=".auth.TasksAccountSettingsActivity"
android:theme="@style/Tasks" />
<activity
android:name=".caldav.CaldavCalendarSettingsActivity"
android:theme="@style/Tasks"/>

@ -17,6 +17,7 @@ import org.tasks.data.CaldavDao
import org.tasks.data.GoogleTaskDao
import org.tasks.filters.NavigationDrawerSubheader
import org.tasks.filters.NavigationDrawerSubheader.SubheaderType
import org.tasks.preferences.MainPreferences
import org.tasks.preferences.Preferences
import org.tasks.preferences.SyncPreferences
import org.tasks.themes.DrawableUtil
@ -45,7 +46,8 @@ internal class SubheaderViewHolder(
when (subheader.subheaderType) {
SubheaderType.PREFERENCE -> preferences.setBoolean(subheader.id.toInt(), collapsed)
SubheaderType.GOOGLE_TASKS -> googleTaskDao.setCollapsed(subheader.id, collapsed)
SubheaderType.CALDAV -> caldavDao.setCollapsed(subheader.id, collapsed)
SubheaderType.CALDAV, SubheaderType.TASKS ->
caldavDao.setCollapsed(subheader.id, collapsed)
}
localBroadcastManager.broadcastRefreshList()
}
@ -64,7 +66,12 @@ internal class SubheaderViewHolder(
init {
ButterKnife.bind(this, itemView)
errorIcon.setOnClickListener {
activity.startActivity(Intent(activity, SyncPreferences::class.java))
val preferenceClass = if (subheader.subheaderType == SubheaderType.TASKS) {
MainPreferences::class.java
} else {
SyncPreferences::class.java
}
activity.startActivity(Intent(activity, preferenceClass))
}
}
}

@ -1,77 +0,0 @@
package org.tasks.auth
import android.app.Activity
import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.caldav.BaseCaldavAccountSettingsActivity
import javax.inject.Inject
@AndroidEntryPoint
class TasksAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Toolbar.OnMenuItemClickListener {
@Inject lateinit var authStateManager: AuthStateManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding.userLayout.visibility = View.GONE
binding.passwordLayout.visibility = View.GONE
binding.urlLayout.visibility = View.GONE
binding.repeat.visibility = View.GONE
}
override val description: Int
get() = 0
override val newPassword: String?
get() = ""
private suspend fun updateAccount(principal: String?) {
hideProgressIndicator()
caldavAccount!!.name = newName
caldavAccount!!.url = principal
caldavAccount!!.username = newUsername
caldavAccount!!.error = ""
if (passwordChanged()) {
caldavAccount!!.password = encryption.encrypt(newPassword!!)
}
caldavAccount!!.isSuppressRepeatingTasks = binding.repeat.isChecked
caldavDao.update(caldavAccount!!)
setResult(Activity.RESULT_OK)
finish()
}
override val needsPurchase: Boolean
get() = !inventory.hasTasksSubscription
override fun hasChanges() =
newName != caldavAccount!!.name
|| binding.repeat.isChecked != caldavAccount!!.isSuppressRepeatingTasks
override fun save() = lifecycleScope.launch {
if (newName.isBlank()) {
binding.nameLayout.error = getString(R.string.name_cannot_be_empty)
return@launch
}
updateAccount()
}
override suspend fun addAccount(url: String, username: String, password: String) {}
override suspend fun updateAccount(url: String, username: String, password: String) {}
override suspend fun updateAccount() = updateAccount(caldavAccount!!.url)
override suspend fun removeAccount() {
authStateManager.signOut()
super.removeAccount()
}
override val helpUrl: String
get() = getString(R.string.help_url_sync)
}

@ -1,5 +1,8 @@
package org.tasks.billing
import android.content.Context
import android.content.Intent
import android.net.Uri
import org.tasks.BuildConfig
import org.tasks.LocalBroadcastManager
import org.tasks.R
@ -70,6 +73,17 @@ class Inventory @Inject constructor(
val hasTasksSubscription: Boolean
get() = subscription?.isTasksSubscription ?: false
fun unsubscribe(context: Context): Boolean {
subscription?.let {
context.startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse(context.getString(R.string.manage_subscription_url, it.sku)))
)
}
return false
}
companion object {
private const val SKU_VIP = "vip"
const val SKU_TASKER = "tasker"

@ -97,16 +97,7 @@ class CaldavSynchronizer @Inject constructor(
setError(account, e.message)
} catch (e: HttpException) {
val message = when(e.code) {
402 -> if (account.isTasksOrg) {
context.getString(if (inventory.subscription == null) {
R.string.your_subscription_expired
} else {
R.string.insufficient_subscription
})
} else {
e.message
}
in 500..599 -> e.message
402, in 500..599 -> e.message
else -> {
firebase.reportException(e)
e.message

@ -239,7 +239,7 @@ class FilterProvider @Inject constructor(
},
account.error?.isNotBlank() ?: false,
account.isCollapsed,
SubheaderType.CALDAV,
if (account.isTasksOrg) SubheaderType.TASKS else SubheaderType.CALDAV,
account.id))
.apply { if (account.isCollapsed) return this }
.plus(caldavDao

@ -123,6 +123,7 @@ public class NavigationDrawerSubheader extends FilterListItem {
public enum SubheaderType {
PREFERENCE,
GOOGLE_TASKS,
CALDAV
CALDAV,
TASKS
}
}

@ -80,19 +80,27 @@ abstract class BasePreferences : ThemedInjectingAppCompatActivity(),
caller: PreferenceFragmentCompat,
pref: Preference
): Boolean {
val args = pref.extras
val fragment = supportFragmentManager.fragmentFactory.instantiate(
classLoader,
pref.fragment
).apply {
arguments = args
setTargetFragment(caller, 0)
}
return startPreference(
caller,
supportFragmentManager
.fragmentFactory
.instantiate(classLoader, pref.fragment)
.apply { arguments = pref.extras },
pref.title
)
}
fun startPreference(
caller: PreferenceFragmentCompat,
fragment: Fragment,
title: CharSequence
): Boolean {
fragment.setTargetFragment(caller, 0)
supportFragmentManager.beginTransaction()
.replace(R.id.settings, fragment)
.addToBackStack(null)
.commit()
toolbar.title = pref.title
.replace(R.id.settings, fragment)
.addToBackStack(null)
.commit()
toolbar.title = title
setupMenu(fragment)
return true
}

@ -60,11 +60,10 @@ class HelpAndFeedback : InjectingPreferenceFragment() {
false
}
findPreference(R.string.refresh_purchases)
.setOnPreferenceClickListener {
billingClient.queryPurchases()
false
}
findPreference(R.string.refresh_purchases).setOnPreferenceClickListener {
billingClient.queryPurchases()
false
}
findPreference(R.string.p_collect_statistics)
.setOnPreferenceClickListener {
@ -73,13 +72,7 @@ class HelpAndFeedback : InjectingPreferenceFragment() {
}
findPreference(R.string.button_unsubscribe).setOnPreferenceClickListener {
inventory.subscription?.let {
startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse(getString(R.string.manage_subscription_url, it.sku))))
}
false
inventory.unsubscribe(requireActivity())
}
findPreference(R.string.upgrade_to_pro).setOnPreferenceClickListener {

@ -12,14 +12,14 @@ import org.tasks.R
import org.tasks.auth.AuthStateManager
import org.tasks.auth.IdToken
import org.tasks.auth.SignInActivity
import org.tasks.auth.TasksAccountSettingsActivity
import org.tasks.caldav.BaseCaldavAccountSettingsActivity
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavDao
import org.tasks.injection.InjectingPreferenceFragment
import org.tasks.preferences.IconPreference
import org.tasks.preferences.MainPreferences
import org.tasks.preferences.Preferences
import org.tasks.preferences.PreferencesViewModel
import org.tasks.preferences.fragments.TasksAccount.Companion.newTasksAccountPreference
import org.tasks.widget.AppWidgetManager
import javax.inject.Inject
@ -93,9 +93,10 @@ class MainSettingsFragment : InjectingPreferenceFragment() {
pref.tint = context?.getColor(R.color.overdue)
}
pref.setOnPreferenceClickListener {
startActivity(
Intent(requireContext(), TasksAccountSettingsActivity::class.java)
.putExtra(BaseCaldavAccountSettingsActivity.EXTRA_CALDAV_DATA, account)
(activity as MainPreferences).startPreference(
this,
newTasksAccountPreference(account),
getString(R.string.tasks_org)
)
false
}

@ -0,0 +1,164 @@
package org.tasks.preferences.fragments
import android.app.Activity.RESULT_OK
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.todoroo.astrid.service.TaskDeleter
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.tasks.BuildConfig
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.auth.AuthStateManager
import org.tasks.billing.BillingClient
import org.tasks.billing.Inventory
import org.tasks.billing.PurchaseDialog
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavDao
import org.tasks.injection.InjectingPreferenceFragment
import java.net.HttpURLConnection.HTTP_PAYMENT_REQUIRED
import javax.inject.Inject
@AndroidEntryPoint
class TasksAccount : InjectingPreferenceFragment() {
@Inject lateinit var authStateManager: AuthStateManager
@Inject lateinit var taskDeleter: TaskDeleter
@Inject lateinit var billingClient: BillingClient
@Inject lateinit var inventory: Inventory
@Inject lateinit var localBroadcastManager: LocalBroadcastManager
@Inject lateinit var caldavDao: CaldavDao
lateinit var caldavAccount: CaldavAccount
private val purchaseReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
lifecycleScope.launch {
if (inventory.subscription?.isTasksSubscription == true
&& caldavAccount.error.isPaymentRequired()) {
caldavAccount.error = null
caldavDao.update(caldavAccount)
}
refreshSubscription()
}
}
}
override fun getPreferenceXml() = R.xml.preferences_tasks
override suspend fun setupPreferences(savedInstanceState: Bundle?) {
caldavAccount = requireArguments().getParcelable(EXTRA_ACCOUNT)!!
findPreference(R.string.logout).setOnPreferenceClickListener {
dialogBuilder
.newDialog()
.setMessage(R.string.logout_warning, getString(R.string.tasks_org))
.setPositiveButton(R.string.remove) { _, _ -> removeAccount() }
.setNegativeButton(R.string.cancel, null)
.show()
false
}
findPreference(R.string.upgrade_to_pro).setOnPreferenceClickListener {
PurchaseDialog
.newPurchaseDialog(this, REQUEST_PURCHASE)
.show(parentFragmentManager, PurchaseDialog.FRAG_TAG_PURCHASE_DIALOG)
false
}
findPreference(R.string.button_unsubscribe).setOnPreferenceClickListener {
inventory.unsubscribe(requireActivity())
}
findPreference(R.string.refresh_purchases).setOnPreferenceClickListener {
billingClient.queryPurchases()
false
}
}
private fun removeAccount() = lifecycleScope.launch {
taskDeleter.delete(caldavAccount)
authStateManager.signOut()
activity?.onBackPressed()
}
override fun onResume() {
super.onResume()
localBroadcastManager.registerPurchaseReceiver(purchaseReceiver)
localBroadcastManager.registerRefreshListReceiver(purchaseReceiver)
refreshSubscription()
}
override fun onPause() {
super.onPause()
localBroadcastManager.unregisterReceiver(purchaseReceiver)
}
private fun refreshSubscription() {
if (BuildConfig.FLAVOR == "generic") {
return
}
val subscription = inventory.subscription
findPreference(R.string.upgrade_to_pro).apply {
if (caldavAccount.error.isPaymentRequired()) {
if (subscription == null) {
setTitle(R.string.upgrade_to_pro)
setSummary(R.string.your_subscription_expired)
} else {
setTitle(R.string.manage_subscription)
setSummary(R.string.insufficient_subscription)
}
} else {
title = getString(
if (subscription == null) {
R.string.upgrade_to_pro
} else {
R.string.manage_subscription
})
summary = if (subscription == null) {
null
} else {
val price = getString(
if (subscription.isMonthly) R.string.price_per_month else R.string.price_per_year,
subscription.subscriptionPrice!! - .01
)
getString(R.string.current_subscription, price)
}
}
}
findPreference(R.string.button_unsubscribe).isEnabled = inventory.subscription != null
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_PURCHASE) {
if (resultCode == RESULT_OK) {
billingClient.queryPurchases()
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
companion object {
private const val REQUEST_PURCHASE = 10201
private const val EXTRA_ACCOUNT = "extra_account"
fun newTasksAccountPreference(account: CaldavAccount): Fragment {
val fragment = TasksAccount()
fragment.arguments = Bundle().apply {
putParcelable(EXTRA_ACCOUNT, account)
}
return fragment
}
private fun String?.isPaymentRequired(): Boolean =
this?.startsWith("HTTP $HTTP_PAYMENT_REQUIRED") == true
}
}

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:key="@string/preference_screen">
<Preference
android:key="@string/sign_in_with_google"
android:title="@string/sign_in_with_google"
app:isPreferenceVisible="false"
tools:isPreferenceVisible="true"/>
<Preference
android:key="@string/upgrade_to_pro"
android:title="@string/upgrade_to_pro"
app:allowDividerAbove="true"
app:icon="@drawable/ic_outline_attach_money_24px" />
<Preference
android:key="@string/button_unsubscribe"
android:title="@string/button_unsubscribe"/>
<Preference
android:key="@string/refresh_purchases"
android:title="@string/refresh_purchases"
app:icon="@drawable/ic_cached_24px" />
<PreferenceCategory
android:key="@string/sync_SPr_interval_title"
android:title="@string/sync_SPr_interval_title">
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/p_background_sync"
android:title="@string/enabled" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:dependency="@string/p_background_sync"
android:key="@string/p_background_sync_unmetered_only"
android:title="@string/background_sync_unmetered_only" />
</PreferenceCategory>
<Preference
android:key="@string/logout"
android:title="@string/logout"
app:allowDividerAbove="true"/>
</PreferenceScreen>
Loading…
Cancel
Save