Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 20 additions & 15 deletions apps/flipcash/core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2025,19 +2025,25 @@

<string name="title_notificationCheckAllowed">Notifications Allowed</string>
<string name="subtitle_notificationCheckAllowed">Allowed in Android settings</string>
<string name="title_notificationCheckNotAllowed">Notifications are turned off</string>
<string name="subtitle_notificationCheckNotAllowedProblem">Notifications are turned off</string>
<string name="subtitle_notificationCheckNotAllowed">Turned off for Flipcash in Android settings</string>
<string name="title_notificationCheckChatOff">Chat notifications are off</string>
<string name="title_notificationCheckChat">Chat Notifications</string>
<string name="title_notificationCheckDeposits">Deposit Notifications</string>
<string name="title_notificationCheckBuySell">Buy and Sell Notifications</string>
<string name="title_notificationCheckGain">Price Alerts</string>
<string name="title_notificationCheckContacts">Contact Notifications</string>
<string name="title_notificationCheckGeneral">Misc. Notifications</string>
<string name="subtitle_notificationCheckChatOffProblem">Chat notifications are off</string>
<string name="subtitle_notificationCheckChatOff">Messages and replies won\'t show</string>
<string name="title_notificationCheckDepositsOff">Deposit notifications are off</string>
<string name="subtitle_notificationCheckDepositsOffProblem">Deposit notifications are off</string>
<string name="subtitle_notificationCheckDepositsOff">Deposits and withdrawals won\'t show</string>
<string name="title_notificationCheckBuySellOff">Buy and sell notifications are off</string>
<string name="subtitle_notificationCheckBuySellOffProblem">Buy and sell notifications are off</string>
<string name="subtitle_notificationCheckBuySellOff">Purchases and sales won\'t show</string>
<string name="title_notificationCheckGainOff">Price alerts are off</string>
<string name="subtitle_notificationCheckGainOffProblem">Price alerts are off</string>
<string name="subtitle_notificationCheckGainOff">Price moves won\'t show</string>
<string name="title_notificationCheckContactsOff">Contact notifications are off</string>
<string name="subtitle_notificationCheckContactsOffProblem">Contact notifications are off</string>
<string name="subtitle_notificationCheckContactsOff">You won\'t hear when contacts join</string>
<string name="title_notificationCheckGeneralOff">Misc. notifications are off</string>
<string name="subtitle_notificationCheckGeneralOffProblem">Misc. notifications are off</string>
<string name="subtitle_notificationCheckGeneralOff">Other updates from Flipcash won\'t show</string>
<string name="title_notificationCheckOtherCategories">Other Categories</string>
<!-- %1$s is a list like "Payments, gains and contacts" -->
Expand All @@ -2051,29 +2057,28 @@
<string name="label_listAnd">%1$s and %2$s</string>
<string name="title_notificationCheckBattery">Battery Optimization</string>
<string name="subtitle_notificationCheckBattery">Flipcash is not optimized</string>
<string name="title_notificationCheckBatteryOn">Battery optimization is on</string>
<string name="subtitle_notificationCheckBatteryOnProblem">Battery optimization is on</string>
<string name="subtitle_notificationCheckBatteryOn">Android may delay notifications</string>
<string name="title_notificationCheckBackground">Background Activity</string>
<!-- %1$s is a standby bucket, e.g. "Active" -->
<string name="subtitle_notificationCheckBackground">Not restricted · standby: %1$s</string>
<string name="subtitle_notificationCheckBackgroundLimited">Android is limiting Flipcash in the background</string>
<string name="title_notificationCheckBackgroundRestricted">Background activity is restricted</string>
<string name="subtitle_notificationCheckBackgroundRestrictedProblem">Background activity is restricted</string>
<string name="subtitle_notificationCheckBackgroundRestricted">Notifications may not arrive</string>
<string name="title_notificationCheckPlayServices">Google Play Services</string>
<string name="subtitle_notificationCheckPlayServices">Available</string>
<string name="subtitle_notificationCheckPlayServicesUpdate">Google Play services needs an update</string>
<string name="title_notificationCheckPlayServicesMissing">Google Play services isn\'t available</string>
<string name="subtitle_notificationCheckPlayServicesMissingProblem">Google Play services isn\'t available</string>
<string name="subtitle_notificationCheckPlayServicesMissing">Push notifications need it</string>
<string name="title_notificationCheckRegistered">Registered for Push</string>
<string name="title_notificationCheckRegistering">Checking registration…</string>
<string name="subtitle_notificationCheckRegistering">Contacting Flipcash</string>
<string name="subtitle_notificationCheckRegistered">Confirmed just now</string>
<string name="title_notificationCheckNoToken">Couldn\'t get a push token from Google</string>
<string name="title_notificationCheckTokenRejected">Flipcash rejected this phone\'s push token</string>
<string name="title_notificationCheckUnreachable">Couldn\'t reach Flipcash</string>
<string name="subtitle_notificationCheckNoTokenProblem">Couldn\'t get a push token from Google</string>
<string name="subtitle_notificationCheckTokenRejectedProblem">Flipcash rejected this phone\'s push token</string>
<string name="subtitle_notificationCheckUnreachableProblem">Couldn\'t reach Flipcash</string>
<string name="subtitle_notificationCheckUnreachable">Check your connection</string>
<!-- %1$s is a relative time, e.g. "2 hours ago" -->
<string name="subtitle_notificationCheckLastConfirmed">Last confirmed %1$s</string>
<string name="subtitle_notificationCheckLastConfirmedJustNow">Last confirmed just now</string>
<string name="title_notificationCheckFailed">Registration failed</string>
<string name="subtitle_notificationCheckFailedProblem">Registration failed</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.flipcash.app.notificationstatus.internal

import androidx.annotation.StringRes
import com.flipcash.app.auth.push.PushRegistrationOutcome
import com.flipcash.core.R

/**
* A row's title: its check name, picked by [key] alone, so it reads the same pending, checking,
* passing or not. The mark and the Fix button show a problem; the title never says it.
*
* @param key a [CheckRow.key]
*/
@StringRes
fun checkTitle(key: String): Int = when (key) {
PermissionKey -> R.string.title_notificationCheckAllowed
OtherChannelsKey -> R.string.title_notificationCheckOtherCategories
BatteryKey -> R.string.title_notificationCheckBattery
BackgroundKey -> R.string.title_notificationCheckBackground
GooglePlayKey -> R.string.title_notificationCheckPlayServices
RegistrationKey -> R.string.title_notificationCheckRegistered
else -> when (StatusChannel.entries.firstOrNull { channelKey(it) == key }) {
StatusChannel.Chat -> R.string.title_notificationCheckChat
StatusChannel.DepositWithdrawal -> R.string.title_notificationCheckDeposits
StatusChannel.BuySell -> R.string.title_notificationCheckBuySell
StatusChannel.Gain -> R.string.title_notificationCheckGain
StatusChannel.ContactJoin -> R.string.title_notificationCheckContacts
StatusChannel.General -> R.string.title_notificationCheckGeneral
// evaluateNotificationStatus gives it no row: it only gates the test button.
StatusChannel.TestPush, null -> error("$key has no row")
}
}

/**
* The sentence that leads a settled row's detail when it has a problem, "Battery optimization is
* on", with any explanation on the line after it. Null for a passing row, whose detail is one line,
* and for a registration still running, whose detail is only "Contacting Flipcash".
*/
@StringRes
fun problemLine(row: CheckRow): Int? = when (row) {
is CheckRow.Pending, is CheckRow.Rechecking, is CheckRow.OtherChannels -> null
is CheckRow.Permission -> R.string.subtitle_notificationCheckNotAllowedProblem.takeUnless { row.allowed }
is CheckRow.BlockedChannel -> when (row.channel) {
StatusChannel.Chat -> R.string.subtitle_notificationCheckChatOffProblem
StatusChannel.DepositWithdrawal -> R.string.subtitle_notificationCheckDepositsOffProblem
StatusChannel.BuySell -> R.string.subtitle_notificationCheckBuySellOffProblem
StatusChannel.Gain -> R.string.subtitle_notificationCheckGainOffProblem
StatusChannel.ContactJoin -> R.string.subtitle_notificationCheckContactsOffProblem
StatusChannel.General -> R.string.subtitle_notificationCheckGeneralOffProblem
StatusChannel.TestPush -> error("${row.channel.id} has no row")
}
is CheckRow.Battery -> R.string.subtitle_notificationCheckBatteryOnProblem.takeIf { row.optimized }
is CheckRow.Background -> when (row.status) {
CheckStatus.Fail -> R.string.subtitle_notificationCheckBackgroundRestrictedProblem
CheckStatus.Warning -> R.string.subtitle_notificationCheckBackgroundLimited
else -> null
}
is CheckRow.GooglePlay -> when (row.status) {
CheckStatus.Fail -> R.string.subtitle_notificationCheckPlayServicesMissingProblem
CheckStatus.Warning -> R.string.subtitle_notificationCheckPlayServicesUpdate
else -> null
}
is CheckRow.Registration -> when (val state = row.state) {
RegistrationState.Checking -> null
is RegistrationState.Finished -> when (state.outcome) {
PushRegistrationOutcome.Ok -> null
PushRegistrationOutcome.NoToken -> R.string.subtitle_notificationCheckNoTokenProblem
PushRegistrationOutcome.InvalidPushToken -> R.string.subtitle_notificationCheckTokenRejectedProblem
PushRegistrationOutcome.Transport -> R.string.subtitle_notificationCheckUnreachableProblem
PushRegistrationOutcome.Failed -> R.string.subtitle_notificationCheckFailedProblem
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -332,16 +332,16 @@ private fun Group(
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
) {
last.row?.let { CheckRowItem(it, state, onFix) }
last.row?.let { CheckRowItem(it, onFix) }
}
}
}
}
}

@Composable
private fun CheckRowItem(row: CheckRow, state: NotificationStatusViewModel.State, onFix: (Fix) -> Unit) {
val text = row.text(state)
private fun CheckRowItem(row: CheckRow, onFix: (Fix) -> Unit) {
val text = row.text()
Row(
modifier = Modifier
.fillMaxWidth()
Expand Down Expand Up @@ -399,91 +399,79 @@ private fun FixButton(fix: Fix, enabled: Boolean, onFix: (Fix) -> Unit) {
)
}

// The title is the check name in every state. The detail leads with the problem, if there is one,
// and puts the explanation on the second line: the two lines the row reserves.
@Composable
private fun CheckRow.text(state: NotificationStatusViewModel.State): Pair<String, String> = when (this) {
is CheckRow.Pending -> stringResource(
when (key) {
PermissionKey -> R.string.title_notificationCheckAllowed
OtherChannelsKey -> R.string.title_notificationCheckOtherCategories
BatteryKey -> R.string.title_notificationCheckBattery
BackgroundKey -> R.string.title_notificationCheckBackground
else -> R.string.title_notificationCheckPlayServices
},
) to stringResource(R.string.title_notificationStatusChecking)
private fun CheckRow.text(): Pair<String, String> {
val title = stringResource(checkTitle(key))
if (this is CheckRow.Pending || this is CheckRow.Rechecking) {
return title to stringResource(R.string.title_notificationStatusChecking)
}
val problem = problemLine(this)?.let { stringResource(it) }
val detail = listOfNotNull(problem, explanation()?.takeIf { it.isNotEmpty() }).joinToString("\n")
return title to detail
}

is CheckRow.Rechecking -> previous.text(state).first to stringResource(R.string.title_notificationStatusChecking)
// The detail's line below the problem, or a passing row's only line. Null when there is none.
@Composable
private fun CheckRow.explanation(): String? = when (this) {
is CheckRow.Pending, is CheckRow.Rechecking -> null

is CheckRow.Permission -> if (allowed) {
stringResource(R.string.title_notificationCheckAllowed) to stringResource(R.string.subtitle_notificationCheckAllowed)
} else {
stringResource(R.string.title_notificationCheckNotAllowed) to stringResource(R.string.subtitle_notificationCheckNotAllowed)
}
is CheckRow.Permission -> stringResource(
if (allowed) R.string.subtitle_notificationCheckAllowed else R.string.subtitle_notificationCheckNotAllowed,
)

is CheckRow.BlockedChannel -> when (channel) {
StatusChannel.Chat -> R.string.title_notificationCheckChatOff to R.string.subtitle_notificationCheckChatOff
StatusChannel.DepositWithdrawal -> R.string.title_notificationCheckDepositsOff to R.string.subtitle_notificationCheckDepositsOff
StatusChannel.BuySell -> R.string.title_notificationCheckBuySellOff to R.string.subtitle_notificationCheckBuySellOff
StatusChannel.Gain -> R.string.title_notificationCheckGainOff to R.string.subtitle_notificationCheckGainOff
StatusChannel.ContactJoin -> R.string.title_notificationCheckContactsOff to R.string.subtitle_notificationCheckContactsOff
StatusChannel.General -> R.string.title_notificationCheckGeneralOff to R.string.subtitle_notificationCheckGeneralOff
StatusChannel.Chat -> R.string.subtitle_notificationCheckChatOff
StatusChannel.DepositWithdrawal -> R.string.subtitle_notificationCheckDepositsOff
StatusChannel.BuySell -> R.string.subtitle_notificationCheckBuySellOff
StatusChannel.Gain -> R.string.subtitle_notificationCheckGainOff
StatusChannel.ContactJoin -> R.string.subtitle_notificationCheckContactsOff
StatusChannel.General -> R.string.subtitle_notificationCheckGeneralOff
// evaluateNotificationStatus gives it no row: it only gates the test button.
StatusChannel.TestPush -> error("${channel.id} has no row")
}.let { (title, detail) -> stringResource(title) to stringResource(detail) }
}.let { stringResource(it) }

is CheckRow.OtherChannels ->
stringResource(R.string.title_notificationCheckOtherCategories) to
stringResource(R.string.subtitle_notificationCheckOtherCategories, categoryList(covered))
stringResource(R.string.subtitle_notificationCheckOtherCategories, categoryList(covered))

is CheckRow.Battery -> if (optimized) {
stringResource(R.string.title_notificationCheckBatteryOn) to stringResource(R.string.subtitle_notificationCheckBatteryOn)
} else {
stringResource(R.string.title_notificationCheckBattery) to stringResource(R.string.subtitle_notificationCheckBattery)
}
is CheckRow.Battery -> stringResource(
if (optimized) R.string.subtitle_notificationCheckBatteryOn else R.string.subtitle_notificationCheckBattery,
)

is CheckRow.Background -> when (status) {
CheckStatus.Fail -> stringResource(R.string.title_notificationCheckBackgroundRestricted) to
stringResource(R.string.subtitle_notificationCheckBackgroundRestricted)
CheckStatus.Warning -> stringResource(R.string.title_notificationCheckBackground) to
stringResource(R.string.subtitle_notificationCheckBackgroundLimited)
else -> stringResource(R.string.title_notificationCheckBackground) to
stringResource(R.string.subtitle_notificationCheckBackground, bucketName(bucket))
CheckStatus.Fail -> stringResource(R.string.subtitle_notificationCheckBackgroundRestricted)
// The problem line says it all.
CheckStatus.Warning -> null
else -> stringResource(R.string.subtitle_notificationCheckBackground, bucketName(bucket))
}

is CheckRow.GooglePlay -> when (status) {
CheckStatus.Fail -> stringResource(R.string.title_notificationCheckPlayServicesMissing) to
stringResource(R.string.subtitle_notificationCheckPlayServicesMissing)
CheckStatus.Warning -> stringResource(R.string.title_notificationCheckPlayServices) to
stringResource(R.string.subtitle_notificationCheckPlayServicesUpdate)
else -> stringResource(R.string.title_notificationCheckPlayServices) to
stringResource(R.string.subtitle_notificationCheckPlayServices)
CheckStatus.Fail -> stringResource(R.string.subtitle_notificationCheckPlayServicesMissing)
CheckStatus.Warning -> null
else -> stringResource(R.string.subtitle_notificationCheckPlayServices)
}

is CheckRow.Registration -> when (val registration = this.state) {
RegistrationState.Checking -> stringResource(R.string.title_notificationCheckRegistering) to
stringResource(R.string.subtitle_notificationCheckRegistering)
RegistrationState.Checking -> stringResource(R.string.subtitle_notificationCheckRegistering)
is RegistrationState.Finished -> when (registration.outcome) {
PushRegistrationOutcome.Ok ->
stringResource(R.string.title_notificationCheckRegistered) to
stringResource(R.string.subtitle_notificationCheckRegistered)
// A failure says what went wrong in the title; these have nothing more to explain.
PushRegistrationOutcome.NoToken -> stringResource(R.string.title_notificationCheckNoToken) to ""
PushRegistrationOutcome.InvalidPushToken ->
stringResource(R.string.title_notificationCheckTokenRejected) to ""
PushRegistrationOutcome.Transport ->
stringResource(R.string.title_notificationCheckUnreachable) to
(registration.lastConfirmedAt?.let {
val confirmedAt = it.toEpochMilliseconds()
// DateUtils says "0 minutes ago" under a minute.
if (System.currentTimeMillis() - confirmedAt < DateUtils.MINUTE_IN_MILLIS) {
stringResource(R.string.subtitle_notificationCheckLastConfirmedJustNow)
} else {
stringResource(
R.string.subtitle_notificationCheckLastConfirmed,
DateUtils.getRelativeTimeSpanString(confirmedAt).toString(),
)
}
} ?: stringResource(R.string.subtitle_notificationCheckUnreachable))
PushRegistrationOutcome.Failed -> stringResource(R.string.title_notificationCheckFailed) to ""
PushRegistrationOutcome.Ok -> stringResource(R.string.subtitle_notificationCheckRegistered)
// The problem line says what went wrong; these have nothing more to explain.
PushRegistrationOutcome.NoToken,
PushRegistrationOutcome.InvalidPushToken,
PushRegistrationOutcome.Failed -> null
PushRegistrationOutcome.Transport -> registration.lastConfirmedAt?.let {
val confirmedAt = it.toEpochMilliseconds()
// DateUtils says "0 minutes ago" under a minute.
if (System.currentTimeMillis() - confirmedAt < DateUtils.MINUTE_IN_MILLIS) {
stringResource(R.string.subtitle_notificationCheckLastConfirmedJustNow)
} else {
stringResource(
R.string.subtitle_notificationCheckLastConfirmed,
DateUtils.getRelativeTimeSpanString(confirmedAt).toString(),
)
}
} ?: stringResource(R.string.subtitle_notificationCheckUnreachable)
}
}
}
Expand Down
Loading
Loading