Add e-mail disclosure dialog

pull/2004/head
Alex Baker 2 years ago
parent 29519c24cc
commit f68ef7cbe5

@ -15,36 +15,33 @@ package org.tasks.auth
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ImageView
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.annotation.MainThread
import androidx.annotation.WorkerThread
import androidx.browser.customtabs.CustomTabsIntent
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.window.Dialog
import androidx.lifecycle.lifecycleScope
import at.bitfire.dav4jvm.exception.HttpException
import com.google.android.material.composethemeadapter.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import net.openid.appauth.AuthState
import net.openid.appauth.AuthorizationException
import net.openid.appauth.AuthorizationRequest
import net.openid.appauth.AuthorizationServiceConfiguration
import net.openid.appauth.ClientSecretBasic
import net.openid.appauth.RegistrationRequest
import net.openid.appauth.RegistrationResponse
import net.openid.appauth.ResponseTypeValues
import net.openid.appauth.*
import org.tasks.R
import org.tasks.Tasks.Companion.IS_GENERIC
import org.tasks.analytics.Firebase
import org.tasks.billing.Inventory
import org.tasks.billing.PurchaseActivity
import org.tasks.billing.PurchaseActivity.Companion.EXTRA_GITHUB
import org.tasks.compose.ConsentDialog
import org.tasks.compose.SignInDialog
import org.tasks.dialogs.DialogBuilder
import org.tasks.extensions.Context.openUri
import org.tasks.injection.InjectingAppCompatActivity
import org.tasks.themes.ThemeColor
import timber.log.Timber
import java.util.concurrent.CountDownLatch
@ -63,7 +60,7 @@ import javax.inject.Inject
* - Initiate the authorization request using the built-in heuristics or a user-selected browser.
*/
@AndroidEntryPoint
class SignInActivity : InjectingAppCompatActivity() {
class SignInActivity : ComponentActivity() {
@Inject lateinit var themeColor: ThemeColor
@Inject lateinit var inventory: Inventory
@Inject lateinit var dialogBuilder: DialogBuilder
@ -86,62 +83,52 @@ class SignInActivity : InjectingAppCompatActivity() {
private val authStateManager: AuthStateManager
get() = authService.authStateManager
enum class Platform {
GOOGLE,
GITHUB,
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.error.observe(this, this::handleError)
val titles = resources.getStringArray(R.array.sign_in_titles)
val summaries = resources.getStringArray(R.array.sign_in_summaries)
val typedArray = resources.obtainTypedArray(R.array.sign_in_icons)
val icons = IntArray(typedArray.length())
for (i in icons.indices) {
icons[i] = typedArray.getResourceId(i, 0)
}
typedArray.recycle()
val adapter = object : BaseAdapter() {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = layoutInflater.inflate(R.layout.simple_list_item_2_themed, null)
val icon = view.findViewById<ImageView>(R.id.image_view)
icon.setImageResource(icons[position])
view.findViewById<TextView>(R.id.text2).text = titles[position]
view.findViewById<TextView>(R.id.text1).text = summaries[position]
if (position == 1) {
icon.drawable.setTint(getColor(R.color.icon_tint))
}
return view
val autoSelect = intent.getSerializableExtra(EXTRA_SELECT_SERVICE) as Platform?
setContent {
var selectedPlatform by rememberSaveable {
mutableStateOf(autoSelect)
}
override fun getCount() = titles.size
override fun getItem(position: Int) = titles[position]
override fun getItemId(position: Int): Long = position.toLong()
}
val autoSelect = intent.getIntExtra(EXTRA_SELECT_SERVICE, -1)
if (autoSelect >= 0 && autoSelect < titles.size) {
selectService(autoSelect)
} else {
dialogBuilder.newDialog()
.setTitle(R.string.sign_in_to_tasks)
.setNeutralButton(R.string.help) { _, _ ->
openUri(R.string.help_url_sync)
finish()
MdcTheme {
selectedPlatform
?.let {
Dialog(onDismissRequest = { finish() }) {
ConsentDialog { agree ->
if (agree) {
selectService(it)
} else {
finish()
}
}
}
}
.setNegativeButton(R.string.cancel) { _, _ ->
finish()
?: Dialog(onDismissRequest = { finish() }) {
SignInDialog(
selected = { selectedPlatform = it },
help = {
openUri(R.string.help_url_sync)
finish()
},
cancel = { finish() }
)
}
.setAdapter(adapter) { _, which -> selectService(which) }
.setOnCancelListener { finish() }
.show()
}
}
}
private fun selectService(which: Int) {
private fun selectService(which: Platform) {
viewModel.initializeAuthService(when (which) {
0 -> AuthorizationService.ISS_GOOGLE
1 -> AuthorizationService.ISS_GITHUB
else -> throw IllegalArgumentException()
Platform.GOOGLE -> AuthorizationService.ISS_GOOGLE
Platform.GITHUB -> AuthorizationService.ISS_GITHUB
})
startAuthorization()
}

@ -0,0 +1,82 @@
package org.tasks.compose
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ContentAlpha
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.google.android.material.composethemeadapter.MdcTheme
import org.tasks.BuildConfig
import org.tasks.R
import org.tasks.sync.AddAccountDialog.Platform
@Composable
fun AddAccountDialog(
hasTasksAccount: Boolean,
selected: (Platform) -> Unit,
) {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
if (!hasTasksAccount) {
SyncAccount(
title = R.string.tasks_org,
description = R.string.tasks_org_description,
icon = R.drawable.ic_round_icon,
onClick = { selected(Platform.TASKS_ORG) }
)
}
SyncAccount(
title = R.string.gtasks_GPr_header,
description = R.string.google_tasks_selection_description,
icon = R.drawable.ic_google,
onClick = { selected(Platform.GOOGLE_TASKS) }
)
if (BuildConfig.DEBUG) {
SyncAccount(
title = R.string.microsoft,
description = R.string.microsoft_selection_description,
icon = R.drawable.ic_microsoft_tasks,
onClick = { selected(Platform.MICROSOFT) }
)
}
SyncAccount(
title = R.string.davx5,
description = R.string.davx5_selection_description,
icon = R.drawable.ic_davx5_icon_green_bg,
onClick = { selected(Platform.DAVX5) }
)
SyncAccount(
title = R.string.caldav,
description = R.string.caldav_selection_description,
icon = R.drawable.ic_webdav_logo,
tint = MaterialTheme.colors.onSurface.copy(
alpha = ContentAlpha.medium
),
onClick = { selected(Platform.CALDAV) }
)
SyncAccount(
title = R.string.etesync,
description = R.string.etesync_selection_description,
icon = R.drawable.ic_etesync,
onClick = { selected(Platform.ETESYNC) }
)
SyncAccount(
title = R.string.decsync,
description = R.string.decsync_selection_description,
icon = R.drawable.ic_decsync,
onClick = { selected(Platform.DECSYNC_CC) }
)
}
}
@Preview(showBackground = true, widthDp = 320)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
@Composable
fun AddAccountDialogPreview() {
MdcTheme {
AddAccountDialog(hasTasksAccount = false, selected = {})
}
}

@ -0,0 +1,114 @@
package org.tasks.compose
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ContentAlpha
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
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 com.google.android.material.composethemeadapter.MdcTheme
import org.tasks.R
import org.tasks.auth.SignInActivity
@Composable
fun SignInDialog(
selected: (SignInActivity.Platform) -> Unit,
help: () -> Unit,
cancel: () -> Unit,
) {
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.background(MaterialTheme.colors.surface)
) {
Text(
text = stringResource(id = R.string.sign_in_to_tasks),
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(16.dp),
)
SyncAccount(
title = R.string.sign_in_with_google,
description = R.string.google_play_subscribers,
icon = R.drawable.ic_google,
onClick = { selected(SignInActivity.Platform.GOOGLE) }
)
SyncAccount(
title = R.string.sign_in_with_github,
description = R.string.github_sponsors,
icon = R.drawable.ic_octocat,
tint = MaterialTheme.colors.onSurface.copy(
alpha = ContentAlpha.medium
),
onClick = { selected(SignInActivity.Platform.GITHUB) }
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
TextButton(onClick = help) {
Text(text = stringResource(id = R.string.help))
}
Spacer(modifier = Modifier.weight(1f))
TextButton(onClick = cancel) {
Text(text = stringResource(id = R.string.cancel))
}
}
}
}
@Composable
fun ConsentDialog(
agree: (Boolean) -> Unit,
) {
Column(Modifier.background(MaterialTheme.colors.surface)) {
Text(
text = stringResource(id = R.string.sign_in_to_tasks),
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(16.dp),
)
Text(
text = stringResource(id = R.string.sign_in_to_tasks_disclosure),
modifier = Modifier.padding(horizontal = 16.dp),
)
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
) {
TextButton(onClick = { agree(false) }) {
Text(text = stringResource(id = R.string.consent_deny))
}
TextButton(onClick = { agree(true) }) {
Text(text = stringResource(id = R.string.consent_agree))
}
}
}
}
@Preview(widthDp = 320)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
@Composable
fun SignInDialogPreview() {
MdcTheme {
SignInDialog(selected = {}, help = {}, cancel = {})
}
}
@Preview(widthDp = 320)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
@Composable
fun DisclosurePreview() {
MdcTheme {
ConsentDialog(agree = {})
}
}

@ -2,20 +2,12 @@ package org.tasks.sync
import android.app.Dialog
import android.os.Bundle
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ContentAlpha
import androidx.compose.material.MaterialTheme
import androidx.compose.ui.Modifier
import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.setFragmentResult
import com.google.android.material.composethemeadapter.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.BuildConfig
import org.tasks.R
import org.tasks.compose.SyncAccount
import org.tasks.dialogs.DialogBuilder
import org.tasks.extensions.Context.openUri
import javax.inject.Inject
@ -44,57 +36,10 @@ class AddAccountDialog : DialogFragment() {
.setTitle(R.string.choose_synchronization_service)
.setContent {
MdcTheme {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
if (!hasTasksAccount) {
SyncAccount(
title = R.string.tasks_org,
description = R.string.tasks_org_description,
icon = R.drawable.ic_round_icon,
onClick = { selected(Platform.TASKS_ORG) }
)
}
SyncAccount(
title = R.string.gtasks_GPr_header,
description = R.string.google_tasks_selection_description,
icon = R.drawable.ic_google,
onClick = { selected(Platform.GOOGLE_TASKS) }
)
if (BuildConfig.DEBUG) {
SyncAccount(
title = R.string.microsoft,
description = R.string.microsoft_selection_description,
icon = R.drawable.ic_microsoft_tasks,
onClick = { selected(Platform.MICROSOFT) }
)
}
SyncAccount(
title = R.string.davx5,
description = R.string.davx5_selection_description,
icon = R.drawable.ic_davx5_icon_green_bg,
onClick = { selected(Platform.DAVX5) }
)
SyncAccount(
title = R.string.caldav,
description = R.string.caldav_selection_description,
icon = R.drawable.ic_webdav_logo,
tint = MaterialTheme.colors.onSurface.copy(
alpha = ContentAlpha.medium
),
onClick = { selected(Platform.CALDAV) }
)
SyncAccount(
title = R.string.etesync,
description = R.string.etesync_selection_description,
icon = R.drawable.ic_etesync,
onClick = { selected(Platform.ETESYNC) }
)
SyncAccount(
title = R.string.decsync,
description = R.string.decsync_selection_description,
icon = R.drawable.ic_decsync,
onClick = { selected(Platform.DECSYNC_CC) }
)
}
org.tasks.compose.AddAccountDialog(
hasTasksAccount = hasTasksAccount,
selected = this::selected
)
}
}
.setNeutralButton(R.string.help) { _, _ -> activity?.openUri(R.string.help_url_sync) }

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?attr/listPreferredItemHeight"
android:mode="twoLine"
android:paddingStart="@dimen/keyline_first"
android:paddingTop="@dimen/list_item_spacing"
android:paddingEnd="@dimen/keyline_first"
android:paddingBottom="@dimen/list_item_spacing">
<ImageView
android:id="@+id/image_view"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentStart="true"
android:layout_marginEnd="@dimen/keyline_first"
app:tint="@null" />
<TextView
android:id="@id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toEndOf="@id/image_view"
android:textAppearance="?attr/textAppearanceListItem" />
<TextView
android:id="@id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/text1"
android:layout_alignStart="@id/text1"
android:textAppearance="?attr/textAppearanceListItemSecondary" />
</TwoLineListItem>

@ -138,21 +138,6 @@
<item>2</item>
</string-array>
<string-array name="sign_in_titles">
<item>@string/google_play_subscribers</item>
<item>@string/github_sponsors</item>
</string-array>
<string-array name="sign_in_summaries">
<item>@string/sign_in_with_google</item>
<item>@string/sign_in_with_github</item>
</string-array>
<array name="sign_in_icons">
<item>@drawable/ic_google</item>
<item>@drawable/ic_octocat</item>
</array>
<string-array name="map_theme_names">
<item>@string/map_theme_use_app_theme</item>
<item>@string/theme_light</item>

@ -669,6 +669,7 @@ File %1$s contained %2$s.\n\n
<string name="above_average">Above average</string>
<string name="save_percent">Save %d%%</string>
<string name="sign_in_to_tasks">Sign in to Tasks.org</string>
<string name="sign_in_to_tasks_disclosure">Your e-mail address and account ID will be transmitted and stored by Tasks.org. This information will be used for authentication and to provide you with important service related announcements. This information will not be shared with anyone.</string>
<string name="app_password">App password</string>
<string name="app_passwords">App passwords</string>
<string name="app_passwords_more_info">Synchronize your tasks and calendars with third-party desktop and mobile apps. Tap here for more info</string>
@ -736,4 +737,6 @@ File %1$s contained %2$s.\n\n
<string name="enable_reminders">Enable reminders</string>
<string name="enable_reminders_description">Reminders are disabled in Android Settings</string>
<string name="sign_in">Sign in</string>
<string name="consent_agree">Agree</string>
<string name="consent_deny">Not now</string>
</resources>

Loading…
Cancel
Save