Merge branch 'dev' into 'master'
Merge Dev See merge request AuroraOSS/AuroraStore!358
This commit is contained in:
@@ -19,11 +19,14 @@
|
|||||||
|
|
||||||
package com.aurora.extensions
|
package com.aurora.extensions
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import javax.annotation.Nullable
|
||||||
|
|
||||||
fun Long.toDate(): String {
|
fun Long.toDate(): String {
|
||||||
val calendar = Calendar.getInstance(Locale.getDefault())
|
val calendar = Calendar.getInstance(Locale.getDefault())
|
||||||
@@ -42,4 +45,51 @@ fun Throwable.stackTraceToString(): String {
|
|||||||
fun isValidPackageName(packageName: String): Boolean {
|
fun isValidPackageName(packageName: String): Boolean {
|
||||||
val packageRegex = "^[a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)*$".toRegex()
|
val packageRegex = "^[a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)*$".toRegex()
|
||||||
return packageName.matches(packageRegex)
|
return packageName.matches(packageRegex)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a darker color from the given color.
|
||||||
|
* @param color The color to darken.
|
||||||
|
* @param factor The factor to darken the color by.
|
||||||
|
* - higher factor values will result in a lighter color.
|
||||||
|
* @return The darker color.
|
||||||
|
*/
|
||||||
|
fun darkenColor(@ColorInt color: Int, factor: Float = 0.25f): Int {
|
||||||
|
val a = Color.alpha(color)
|
||||||
|
val r = (Color.red(color) * factor).coerceIn(0f, 255f).toInt()
|
||||||
|
val g = (Color.green(color) * factor).coerceIn(0f, 255f).toInt()
|
||||||
|
val b = (Color.blue(color) * factor).coerceIn(0f, 255f).toInt()
|
||||||
|
|
||||||
|
return Color.argb(a, r, g, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a lighter color from the given color.
|
||||||
|
* @param color The color to lighten.
|
||||||
|
* @param factor The factor to lighten the color by.
|
||||||
|
* - higher factor values will result in a lighter color.
|
||||||
|
* @param alpha The alpha value to use for the lighter color.
|
||||||
|
* @return The lighter color.
|
||||||
|
*/
|
||||||
|
fun lightenColor(@ColorInt color: Int, factor: Float = 0.5f, @Nullable alpha: Int? = null): Int {
|
||||||
|
val a = alpha ?: Color.alpha(color)
|
||||||
|
val r = (Color.red(color) + (255 - Color.red(color)) * factor).toInt()
|
||||||
|
val g = (Color.green(color) + (255 - Color.green(color)) * factor).toInt()
|
||||||
|
val b = (Color.blue(color) + (255 - Color.blue(color)) * factor).toInt()
|
||||||
|
|
||||||
|
return Color.argb(a, r, g, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a contrasting color from the given color.
|
||||||
|
* @param color The color to contrast.
|
||||||
|
* @return The contrasting color.
|
||||||
|
*/
|
||||||
|
fun contrastingColor(@ColorInt color: Int): Int {
|
||||||
|
val red = Color.red(color)
|
||||||
|
val green = Color.green(color)
|
||||||
|
val blue = Color.blue(color)
|
||||||
|
val yiq = ((red * 299) + (green * 587) + (blue * 114)) / 1000
|
||||||
|
|
||||||
|
return if (yiq >= 128) Color.BLACK else Color.WHITE
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,8 @@ package com.aurora.store.view.custom.layouts
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.databinding.ViewDevInfoBinding
|
import com.aurora.store.databinding.ViewDevInfoBinding
|
||||||
|
|
||||||
@@ -29,6 +31,16 @@ class DevInfoLayout : RelativeLayout {
|
|||||||
|
|
||||||
private lateinit var binding: ViewDevInfoBinding
|
private lateinit var binding: ViewDevInfoBinding
|
||||||
|
|
||||||
|
val icon: AppCompatImageView get() = binding.img
|
||||||
|
|
||||||
|
var title: String
|
||||||
|
get() = binding.txtTitle.text.toString()
|
||||||
|
set(value) = setTxtTitle(value)
|
||||||
|
|
||||||
|
var subTitle: String?
|
||||||
|
get() = binding.txtSubtitle.text.toString()
|
||||||
|
set(value) = setTxtSubtitle(value)
|
||||||
|
|
||||||
constructor(context: Context) : super(context) {
|
constructor(context: Context) : super(context) {
|
||||||
init(context, null)
|
init(context, null)
|
||||||
}
|
}
|
||||||
@@ -64,8 +76,15 @@ class DevInfoLayout : RelativeLayout {
|
|||||||
typedArray.recycle()
|
typedArray.recycle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setTxtTitle(text: String?) {
|
||||||
|
binding.txtTitle.text = text
|
||||||
|
binding.txtTitle.isVisible = text != null
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
fun setTxtSubtitle(text: String?) {
|
fun setTxtSubtitle(text: String?) {
|
||||||
binding.txtSubtitle.text = text
|
binding.txtSubtitle.text = text
|
||||||
|
binding.txtSubtitle.isVisible = text != null
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,13 @@
|
|||||||
package com.aurora.store.view.custom.layouts.button
|
package com.aurora.store.view.custom.layouts.button
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
|
import com.aurora.extensions.accentColor
|
||||||
|
import com.aurora.extensions.darkenColor
|
||||||
import com.aurora.extensions.getString
|
import com.aurora.extensions.getString
|
||||||
|
import com.aurora.extensions.lightenColor
|
||||||
import com.aurora.extensions.runOnUiThread
|
import com.aurora.extensions.runOnUiThread
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.model.DownloadStatus
|
import com.aurora.store.data.model.DownloadStatus
|
||||||
@@ -51,6 +55,15 @@ class UpdateButton : RelativeLayout {
|
|||||||
private fun init(context: Context) {
|
private fun init(context: Context) {
|
||||||
val view = inflate(context, R.layout.view_update_button, this)
|
val view = inflate(context, R.layout.view_update_button, this)
|
||||||
binding = ViewUpdateButtonBinding.bind(view)
|
binding = ViewUpdateButtonBinding.bind(view)
|
||||||
|
|
||||||
|
// Apply primaryColor tint to all buttons with alpha
|
||||||
|
val alphaAccent = lightenColor(context.accentColor(), alpha = 200)
|
||||||
|
binding.btnPositive.backgroundTintList = ColorStateList.valueOf(alphaAccent)
|
||||||
|
binding.btnNegative.backgroundTintList = ColorStateList.valueOf(alphaAccent)
|
||||||
|
|
||||||
|
val textColor = darkenColor(context.accentColor())
|
||||||
|
binding.btnPositive.setTextColor(textColor)
|
||||||
|
binding.btnNegative.setTextColor(textColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setText(text: String) {
|
fun setText(text: String) {
|
||||||
@@ -65,9 +78,8 @@ class UpdateButton : RelativeLayout {
|
|||||||
|
|
||||||
fun updateState(downloadStatus: DownloadStatus) {
|
fun updateState(downloadStatus: DownloadStatus) {
|
||||||
val displayChild = when (downloadStatus) {
|
val displayChild = when (downloadStatus) {
|
||||||
DownloadStatus.QUEUED,
|
DownloadStatus.QUEUED -> 1
|
||||||
DownloadStatus.DOWNLOADING -> 2
|
DownloadStatus.DOWNLOADING -> 2
|
||||||
|
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import com.airbnb.epoxy.CallbackProp
|
|||||||
import com.airbnb.epoxy.ModelProp
|
import com.airbnb.epoxy.ModelProp
|
||||||
import com.airbnb.epoxy.ModelView
|
import com.airbnb.epoxy.ModelView
|
||||||
import com.airbnb.epoxy.OnViewRecycled
|
import com.airbnb.epoxy.OnViewRecycled
|
||||||
|
import com.aurora.extensions.accentColor
|
||||||
|
import com.aurora.extensions.contrastingColor
|
||||||
import com.aurora.store.databinding.ViewHeaderUpdateBinding
|
import com.aurora.store.databinding.ViewHeaderUpdateBinding
|
||||||
|
|
||||||
|
|
||||||
@@ -46,6 +48,7 @@ class UpdateHeaderView @JvmOverloads constructor(
|
|||||||
@ModelProp
|
@ModelProp
|
||||||
fun action(action: String) {
|
fun action(action: String) {
|
||||||
binding.btnAction.text = action
|
binding.btnAction.text = action
|
||||||
|
binding.btnAction.setTextColor(contrastingColor(context.accentColor()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@CallbackProp
|
@CallbackProp
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import androidx.core.view.isVisible
|
|||||||
import com.airbnb.epoxy.CallbackProp
|
import com.airbnb.epoxy.CallbackProp
|
||||||
import com.airbnb.epoxy.ModelProp
|
import com.airbnb.epoxy.ModelProp
|
||||||
import com.airbnb.epoxy.ModelView
|
import com.airbnb.epoxy.ModelView
|
||||||
|
import com.aurora.extensions.accentColor
|
||||||
|
import com.aurora.extensions.contrastingColor
|
||||||
import com.aurora.store.databinding.ViewNoAppBinding
|
import com.aurora.store.databinding.ViewNoAppBinding
|
||||||
import com.aurora.store.view.epoxy.views.BaseModel
|
import com.aurora.store.view.epoxy.views.BaseModel
|
||||||
import com.aurora.store.view.epoxy.views.BaseView
|
import com.aurora.store.view.epoxy.views.BaseView
|
||||||
@@ -62,6 +64,7 @@ class NoAppView @JvmOverloads constructor(
|
|||||||
@ModelProp
|
@ModelProp
|
||||||
fun actionMessage(message: String = String()) {
|
fun actionMessage(message: String = String()) {
|
||||||
binding.button.text = message
|
binding.button.text = message
|
||||||
|
binding.button.setTextColor(contrastingColor(context.accentColor()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.os.Bundle
|
|||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -33,6 +34,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -50,7 +52,11 @@ import coil.compose.SubcomposeAsyncImage
|
|||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.Constants.URL_TOS
|
import com.aurora.Constants.URL_TOS
|
||||||
|
import com.aurora.extensions.accentColor
|
||||||
import com.aurora.extensions.browse
|
import com.aurora.extensions.browse
|
||||||
|
import com.aurora.extensions.darkenColor
|
||||||
|
import com.aurora.extensions.getStyledAttributeColor
|
||||||
|
import com.aurora.extensions.lightenColor
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
import com.aurora.store.data.providers.AuthProvider
|
||||||
import com.aurora.store.view.theme.AuroraTheme
|
import com.aurora.store.view.theme.AuroraTheme
|
||||||
@@ -64,6 +70,11 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var authProvider: AuthProvider
|
lateinit var authProvider: AuthProvider
|
||||||
|
|
||||||
|
private var primaryColor: Color = Color.White
|
||||||
|
private var onPrimaryColor: Color = Color.Black
|
||||||
|
private var secondaryColor: Color = Color.White
|
||||||
|
private var onSecondaryColor: Color = Color.Black
|
||||||
|
|
||||||
private data class Option(
|
private data class Option(
|
||||||
@StringRes val title: Int,
|
@StringRes val title: Int,
|
||||||
@DrawableRes val icon: Int,
|
@DrawableRes val icon: Int,
|
||||||
@@ -81,30 +92,37 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||||
setContent {
|
setContent {
|
||||||
AuroraTheme {
|
AuroraTheme {
|
||||||
val backgroundColor = if (isSystemInDarkTheme()) {
|
if (isSystemInDarkTheme()) {
|
||||||
MaterialTheme.colorScheme.onSecondary
|
primaryColor = Color(darkenColor(requireContext().accentColor(), 0.25f))
|
||||||
|
onPrimaryColor = Color(lightenColor(primaryColor.toArgb()))
|
||||||
|
secondaryColor = Color(darkenColor(requireContext().accentColor(), 0.15f))
|
||||||
|
onSecondaryColor = Color(lightenColor(primaryColor.toArgb()))
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colorScheme.surfaceVariant
|
primaryColor = Color(lightenColor(requireContext().accentColor(), 0.85f))
|
||||||
|
onPrimaryColor = Color(darkenColor(primaryColor.toArgb()))
|
||||||
|
secondaryColor = Color(lightenColor(requireContext().accentColor(), 0.95f))
|
||||||
|
onSecondaryColor = Color(darkenColor(primaryColor.toArgb()))
|
||||||
}
|
}
|
||||||
|
|
||||||
val onBackgroundColor = if (isSystemInDarkTheme()) {
|
|
||||||
MaterialTheme.colorScheme.onSurface
|
|
||||||
} else {
|
|
||||||
MaterialTheme.colorScheme.onSecondary
|
|
||||||
}
|
|
||||||
|
|
||||||
val tintColor = if (isSystemInDarkTheme()) Color.White else Color.Black
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(color = backgroundColor)
|
.background(color = primaryColor)
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(10.dp),
|
.padding(10.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterVertically)
|
verticalArrangement = Arrangement.spacedBy(
|
||||||
|
5.dp,
|
||||||
|
Alignment.CenterVertically
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
AppBar(tintColor = tintColor)
|
AppBar(
|
||||||
AccountHeader(backgroundColor = onBackgroundColor)
|
backgroundColor = primaryColor,
|
||||||
|
onBackgroundColor = onPrimaryColor
|
||||||
|
)
|
||||||
|
AccountHeader(
|
||||||
|
backgroundColor = secondaryColor,
|
||||||
|
onBackgroundColor = onSecondaryColor
|
||||||
|
)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(
|
.clip(
|
||||||
@@ -115,17 +133,22 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
bottomEnd = 20.dp
|
bottomEnd = 20.dp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.background(color = onBackgroundColor)
|
.background(color = secondaryColor)
|
||||||
) {
|
) {
|
||||||
getOptions().fastForEach { option -> OptionItem(option = option) }
|
getOptions().fastForEach { option ->
|
||||||
|
OptionItem(
|
||||||
|
option = option,
|
||||||
|
tintColor = onSecondaryColor
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
getExtraOptions().fastForEach { option ->
|
getExtraOptions().fastForEach { option ->
|
||||||
OptionItem(
|
OptionItem(
|
||||||
option = option,
|
option = option,
|
||||||
tintColor = tintColor
|
tintColor = onPrimaryColor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Footer(tintColor)
|
Footer(onPrimaryColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,20 +156,20 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppBar(tintColor: Color) {
|
fun AppBar(backgroundColor: Color = Color.Transparent, onBackgroundColor: Color) {
|
||||||
Box(contentAlignment = Alignment.CenterEnd) {
|
Box(contentAlignment = Alignment.CenterEnd) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
text = stringResource(id = R.string.app_name),
|
text = stringResource(id = R.string.app_name),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
color = tintColor,
|
color = onBackgroundColor,
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
IconButton(onClick = { findNavController().navigateUp() }) {
|
IconButton(onClick = { findNavController().navigateUp() }) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = R.drawable.ic_cancel),
|
painter = painterResource(id = R.drawable.ic_cancel),
|
||||||
contentDescription = stringResource(id = R.string.action_cancel),
|
contentDescription = stringResource(id = R.string.action_cancel),
|
||||||
tint = tintColor
|
tint = onBackgroundColor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,11 +180,12 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterHorizontally)
|
horizontalArrangement = Arrangement.spacedBy(2.dp, Alignment.CenterHorizontally)
|
||||||
) {
|
) {
|
||||||
TextButton(onClick = { requireContext().browse(Constants.URL_POLICY) }) {
|
TextButton(onClick = { requireContext().browse(Constants.URL_POLICY) }) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.privacy_policy_title),
|
text = stringResource(id = R.string.privacy_policy_title),
|
||||||
|
fontWeight = FontWeight.Light,
|
||||||
color = tintColor
|
color = tintColor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -169,6 +193,7 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
TextButton(onClick = { requireContext().browse(URL_TOS) }) {
|
TextButton(onClick = { requireContext().browse(URL_TOS) }) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.menu_terms),
|
text = stringResource(id = R.string.menu_terms),
|
||||||
|
fontWeight = FontWeight.Light,
|
||||||
color = tintColor
|
color = tintColor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -176,13 +201,16 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AccountHeader(backgroundColor: Color) {
|
private fun AccountHeader(backgroundColor: Color, onBackgroundColor: Color = Color.White) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clip(
|
.clip(
|
||||||
RoundedCornerShape(
|
RoundedCornerShape(
|
||||||
topStart = 20.dp, topEnd = 20.dp, bottomStart = 5.dp, bottomEnd = 5.dp
|
topStart = 20.dp,
|
||||||
|
topEnd = 20.dp,
|
||||||
|
bottomStart = 5.dp,
|
||||||
|
bottomEnd = 5.dp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.background(color = backgroundColor)
|
.background(color = backgroundColor)
|
||||||
@@ -205,7 +233,7 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
contentDescription = stringResource(id = R.string.title_account_manager),
|
contentDescription = stringResource(id = R.string.title_account_manager),
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.requiredSize(48.dp)
|
.requiredSize(36.dp)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
)
|
)
|
||||||
Column(
|
Column(
|
||||||
@@ -213,24 +241,32 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
horizontalAlignment = Alignment.Start
|
horizontalAlignment = Alignment.Start
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = if (authProvider.isAnonymous) "anonymous" else authProvider.authData!!.userProfile!!.name,
|
text = if (authProvider.isAnonymous) "Anonymous" else authProvider.authData!!.userProfile!!.name,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.Normal,
|
||||||
fontSize = 16.sp
|
fontSize = 16.sp,
|
||||||
|
color = onBackgroundColor
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = if (authProvider.isAnonymous) "anonymous@gmail.com" else authProvider.authData!!.userProfile!!.email,
|
text = if (authProvider.isAnonymous) "anonymous@gmail.com" else authProvider.authData!!.userProfile!!.email,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Light,
|
||||||
fontSize = 14.sp
|
fontSize = 14.sp,
|
||||||
|
color = onBackgroundColor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onClick = { findNavController().navigate(R.id.accountFragment) },
|
onClick = { findNavController().navigate(R.id.accountFragment) },
|
||||||
shape = RoundedCornerShape(12.dp)
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
border = BorderStroke(
|
||||||
|
1.dp,
|
||||||
|
Color(requireContext().getStyledAttributeColor(androidx.appcompat.R.attr.colorControlHighlight))
|
||||||
|
),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.manage_account),
|
text = stringResource(id = R.string.manage_account),
|
||||||
color = Color.Black
|
color = onBackgroundColor,
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ import coil.load
|
|||||||
import coil.transform.RoundedCornersTransformation
|
import coil.transform.RoundedCornersTransformation
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.Constants.EXODUS_SUBMIT_PAGE
|
import com.aurora.Constants.EXODUS_SUBMIT_PAGE
|
||||||
|
import com.aurora.extensions.accentColor
|
||||||
import com.aurora.extensions.browse
|
import com.aurora.extensions.browse
|
||||||
|
import com.aurora.extensions.contrastingColor
|
||||||
import com.aurora.extensions.getString
|
import com.aurora.extensions.getString
|
||||||
import com.aurora.extensions.hide
|
import com.aurora.extensions.hide
|
||||||
import com.aurora.extensions.runOnUiThread
|
import com.aurora.extensions.runOnUiThread
|
||||||
@@ -51,6 +53,7 @@ import com.aurora.gplayapi.data.models.File
|
|||||||
import com.aurora.gplayapi.data.models.Review
|
import com.aurora.gplayapi.data.models.Review
|
||||||
import com.aurora.gplayapi.data.models.StreamBundle
|
import com.aurora.gplayapi.data.models.StreamBundle
|
||||||
import com.aurora.gplayapi.data.models.StreamCluster
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
|
import com.aurora.gplayapi.data.models.datasafety.EntryType
|
||||||
import com.aurora.store.AppStreamStash
|
import com.aurora.store.AppStreamStash
|
||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.PermissionType
|
import com.aurora.store.PermissionType
|
||||||
@@ -89,6 +92,7 @@ import kotlinx.coroutines.flow.filter
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import com.aurora.gplayapi.data.models.datasafety.Report as DataSafetyReport
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||||
@@ -264,6 +268,11 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Data Safety Report
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
viewModel.dataSafetyReport.collect { updateDataSafetyViews(it) }
|
||||||
|
}
|
||||||
|
|
||||||
// Exodus Privacy Report
|
// Exodus Privacy Report
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.exodusReport.collect { report ->
|
viewModel.exodusReport.collect { report ->
|
||||||
@@ -310,7 +319,10 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.testingProgramStatus.collect {
|
viewModel.testingProgramStatus.collect {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
binding.layoutDetailsBeta.btnBetaAction.isEnabled = true
|
binding.layoutDetailsBeta.btnBetaAction.apply {
|
||||||
|
isEnabled = true
|
||||||
|
setTextColor(contrastingColor(requireContext().accentColor()))
|
||||||
|
}
|
||||||
if (it.subscribed) {
|
if (it.subscribed) {
|
||||||
updateBetaActions(true)
|
updateBetaActions(true)
|
||||||
}
|
}
|
||||||
@@ -342,8 +354,11 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Misc Bindings
|
// Misc Bindings
|
||||||
binding.layoutDetailsPrivacy.btnRequestAnalysis.setOnClickListener {
|
binding.layoutDetailsPrivacy.btnRequestAnalysis.apply {
|
||||||
it.context.browse("${EXODUS_SUBMIT_PAGE}${app.packageName}")
|
setOnClickListener {
|
||||||
|
it.context.browse("${EXODUS_SUBMIT_PAGE}${app.packageName}")
|
||||||
|
}
|
||||||
|
setTextColor(contrastingColor(requireContext().accentColor()))
|
||||||
}
|
}
|
||||||
binding.layoutDetailsInstall.progressDownload.clipToOutline = true
|
binding.layoutDetailsInstall.progressDownload.clipToOutline = true
|
||||||
binding.layoutDetailsInstall.imgCancel.setOnClickListener {
|
binding.layoutDetailsInstall.imgCancel.setOnClickListener {
|
||||||
@@ -687,6 +702,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
inflateAppDescription(app)
|
inflateAppDescription(app)
|
||||||
inflateAppRatingAndReviews(app)
|
inflateAppRatingAndReviews(app)
|
||||||
inflateAppDevInfo(app)
|
inflateAppDevInfo(app)
|
||||||
|
inflateAppDataSafety(app)
|
||||||
inflateAppPrivacy(app)
|
inflateAppPrivacy(app)
|
||||||
inflateAppPermission(app)
|
inflateAppPermission(app)
|
||||||
|
|
||||||
@@ -814,6 +830,10 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun inflateAppDataSafety(app: App) {
|
||||||
|
viewModel.fetchAppDataSafetyReport(app.packageName)
|
||||||
|
}
|
||||||
|
|
||||||
private fun inflateAppPrivacy(app: App) {
|
private fun inflateAppPrivacy(app: App) {
|
||||||
viewModel.fetchAppReport(app.packageName)
|
viewModel.fetchAppReport(app.packageName)
|
||||||
}
|
}
|
||||||
@@ -953,6 +973,32 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateDataSafetyViews(report: DataSafetyReport) {
|
||||||
|
report.entries.groupBy { it.type }.forEach { (type, entries) ->
|
||||||
|
when (type) {
|
||||||
|
EntryType.DATA_COLLECTED -> {
|
||||||
|
binding.layoutDetailsDataSafety.dataCollect.title = HtmlCompat.fromHtml(
|
||||||
|
entries.first().description,
|
||||||
|
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||||
|
).toString()
|
||||||
|
binding.layoutDetailsDataSafety.dataCollect.subTitle =
|
||||||
|
entries.first().subEntries.joinToString(", ") { it.name }.ifBlank { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
EntryType.DATA_SHARED -> {
|
||||||
|
binding.layoutDetailsDataSafety.dataShare.title = HtmlCompat.fromHtml(
|
||||||
|
entries.first().description,
|
||||||
|
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||||
|
).toString()
|
||||||
|
binding.layoutDetailsDataSafety.dataShare.subTitle =
|
||||||
|
entries.first().subEntries.joinToString(", ") { it.name }.ifBlank { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* App Review Helpers */
|
/* App Review Helpers */
|
||||||
|
|
||||||
private fun addAvgReviews(number: Int, max: Long, rating: Long): RelativeLayout {
|
private fun addAvgReviews(number: Int, max: Long, rating: Long): RelativeLayout {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.aurora.gplayapi.data.models.Review
|
|||||||
import com.aurora.gplayapi.data.models.details.TestingProgramStatus
|
import com.aurora.gplayapi.data.models.details.TestingProgramStatus
|
||||||
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
||||||
import com.aurora.gplayapi.helpers.ReviewsHelper
|
import com.aurora.gplayapi.helpers.ReviewsHelper
|
||||||
|
import com.aurora.gplayapi.helpers.web.WebDataSafetyHelper
|
||||||
import com.aurora.store.data.model.ExodusReport
|
import com.aurora.store.data.model.ExodusReport
|
||||||
import com.aurora.store.data.model.Report
|
import com.aurora.store.data.model.Report
|
||||||
import com.aurora.store.data.network.HttpClient
|
import com.aurora.store.data.network.HttpClient
|
||||||
@@ -28,6 +29,7 @@ import kotlinx.coroutines.launch
|
|||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import com.aurora.gplayapi.data.models.datasafety.Report as DataSafetyReport
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class AppDetailsViewModel @Inject constructor(
|
class AppDetailsViewModel @Inject constructor(
|
||||||
@@ -58,6 +60,10 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
private val _userReview = MutableSharedFlow<Review>()
|
private val _userReview = MutableSharedFlow<Review>()
|
||||||
val userReview = _userReview.asSharedFlow()
|
val userReview = _userReview.asSharedFlow()
|
||||||
|
|
||||||
|
private val dataSafetyReportStash = mutableMapOf<String, DataSafetyReport>()
|
||||||
|
private val _dataSafetyReport = MutableSharedFlow<DataSafetyReport>()
|
||||||
|
val dataSafetyReport = _dataSafetyReport.asSharedFlow()
|
||||||
|
|
||||||
private val exodusReportStash = mutableMapOf<String, Report?>()
|
private val exodusReportStash = mutableMapOf<String, Report?>()
|
||||||
private val _exodusReport = MutableSharedFlow<Report?>()
|
private val _exodusReport = MutableSharedFlow<Report?>()
|
||||||
val exodusReport = _exodusReport.asSharedFlow()
|
val exodusReport = _exodusReport.asSharedFlow()
|
||||||
@@ -103,6 +109,21 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun fetchAppDataSafetyReport(packageName: String) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val report = dataSafetyReportStash.getOrPut(packageName) {
|
||||||
|
WebDataSafetyHelper()
|
||||||
|
.using(httpClient)
|
||||||
|
.fetch(packageName)
|
||||||
|
}
|
||||||
|
_dataSafetyReport.emit(report)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.e(TAG, "Failed to fetch data safety report", exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun fetchAppReport(packageName: String) {
|
fun fetchAppReport(packageName: String) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
11
app/src/main/res/drawable/ic_cloud_upload.xml
Normal file
11
app/src/main/res/drawable/ic_cloud_upload.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M260,800Q169,800 104.5,737Q40,674 40,583Q40,505 87,444Q134,383 210,366Q235,274 310,217Q385,160 480,160Q597,160 678.5,241.5Q760,323 760,440L760,440L760,440Q829,448 874.5,499.5Q920,551 920,620Q920,695 867.5,747.5Q815,800 740,800L520,800Q487,800 463.5,776.5Q440,753 440,720L440,514L376,576L320,520L480,360L640,520L584,576L520,514L520,720Q520,720 520,720Q520,720 520,720L740,720Q782,720 811,691Q840,662 840,620Q840,578 811,549Q782,520 740,520L680,520L680,440Q680,357 621.5,298.5Q563,240 480,240Q397,240 338.5,298.5Q280,357 280,440L260,440Q202,440 161,481Q120,522 120,580Q120,638 161,679Q202,720 260,720L360,720L360,800L260,800ZM480,520L480,520L480,520Q480,520 480,520Q480,520 480,520L480,520L480,520Q480,520 480,520Q480,520 480,520L480,520Q480,520 480,520Q480,520 480,520L480,520Q480,520 480,520Q480,520 480,520Q480,520 480,520Q480,520 480,520L480,520L480,520Q480,520 480,520Q480,520 480,520Q480,520 480,520Q480,520 480,520L480,520Q480,520 480,520Q480,520 480,520Q480,520 480,520Q480,520 480,520L480,520Z"/>
|
||||||
|
</vector>
|
||||||
@@ -103,6 +103,10 @@
|
|||||||
android:id="@+id/layout_details_permissions"
|
android:id="@+id/layout_details_permissions"
|
||||||
layout="@layout/layout_details_permissions" />
|
layout="@layout/layout_details_permissions" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/layout_details_data_safety"
|
||||||
|
layout="@layout/layout_details_data_safety" />
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/layout_details_privacy"
|
android:id="@+id/layout_details_privacy"
|
||||||
layout="@layout/layout_details_privacy" />
|
layout="@layout/layout_details_privacy" />
|
||||||
|
|||||||
35
app/src/main/res/layout/layout_details_data_safety.xml
Normal file
35
app/src/main/res/layout/layout_details_data_safety.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout 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:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:divider="@drawable/divider"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="@dimen/padding_small"
|
||||||
|
android:paddingEnd="@dimen/padding_small"
|
||||||
|
android:showDividers="middle">
|
||||||
|
|
||||||
|
<com.aurora.store.view.custom.layouts.ActionHeaderLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:headerSubtitle="@string/details_data_safety_subtitle"
|
||||||
|
app:headerTitle="@string/details_data_safety_title" />
|
||||||
|
|
||||||
|
<com.aurora.store.view.custom.layouts.DevInfoLayout
|
||||||
|
android:id="@+id/data_collect"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:imgIcon="@drawable/ic_cloud_upload"
|
||||||
|
tools:txtSubtitle="Personal info, Financial info and 3 others"
|
||||||
|
tools:txtTitle="This app may collect these data types" />
|
||||||
|
|
||||||
|
<com.aurora.store.view.custom.layouts.DevInfoLayout
|
||||||
|
android:id="@+id/data_share"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:imgIcon="@drawable/ic_share"
|
||||||
|
tools:txtSubtitle="Learn more about how developers declare sharing"
|
||||||
|
tools:txtTitle="No data shared with third parties" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -106,6 +106,7 @@
|
|||||||
android:layout_alignTop="@id/txt_line1"
|
android:layout_alignTop="@id/txt_line1"
|
||||||
android:layout_toStartOf="@id/btn_action"
|
android:layout_toStartOf="@id/btn_action"
|
||||||
android:contentDescription="@string/details_changelog"
|
android:contentDescription="@string/details_changelog"
|
||||||
|
app:iconTint="?colorControlNormal"
|
||||||
app:icon="@drawable/ic_arrow_down" />
|
app:icon="@drawable/ic_arrow_down" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.layouts.button.UpdateButton
|
<com.aurora.store.view.custom.layouts.button.UpdateButton
|
||||||
|
|||||||
@@ -45,5 +45,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:text="@string/action_update_all" />
|
android:text="@string/action_update_all"
|
||||||
|
android:textAppearance="@style/TextAppearance.Aurora.Line1" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|||||||
@@ -41,12 +41,13 @@
|
|||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
||||||
tools:text="No updates available" />
|
tools:text="No updates available" />
|
||||||
|
|
||||||
<Button
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/button"
|
android:id="@+id/button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/txt"
|
android:layout_below="@id/txt"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:text="@string/check_updates" />
|
tools:text="@string/check_updates"
|
||||||
|
tools:visibility="visible" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|||||||
@@ -25,14 +25,16 @@
|
|||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btn"
|
android:id="@+id/btn"
|
||||||
style="@style/Widget.Material3.Button.IconButton.Outlined"
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
|
android:textColor="?colorControlNormal"
|
||||||
app:iconPadding="@dimen/padding_normal"
|
app:iconPadding="@dimen/padding_normal"
|
||||||
app:iconTint="?colorAccent"
|
app:iconTint="?colorControlNormal"
|
||||||
app:strokeColor="?colorAccent"
|
app:strokeColor="?colorControlHighlight"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
tools:icon="@drawable/ic_anonymous"
|
tools:icon="@drawable/ic_anonymous"
|
||||||
tools:text="Button Name" />
|
tools:text="Button Name" />
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btnPositive"
|
android:id="@+id/btnPositive"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/action_update"
|
android:text="@string/action_update"
|
||||||
@@ -48,6 +49,7 @@
|
|||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btnQueued"
|
android:id="@+id/btnQueued"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
@@ -61,15 +63,11 @@
|
|||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btnNegative"
|
android:id="@+id/btnNegative"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/action_cancel"
|
android:text="@string/action_cancel"
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1" />
|
android:textAppearance="@style/TextAppearance.Aurora.Line1" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</ViewFlipper>
|
</ViewFlipper>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,8 @@
|
|||||||
<string name="details_no_apps_on_sale">"No apps found on sale"</string>
|
<string name="details_no_apps_on_sale">"No apps found on sale"</string>
|
||||||
<string name="details_paid">"Paid"</string>
|
<string name="details_paid">"Paid"</string>
|
||||||
<string name="details_permission">"Permission"</string>
|
<string name="details_permission">"Permission"</string>
|
||||||
|
<string name="details_data_safety_title">Data safety</string>
|
||||||
|
<string name="details_data_safety_subtitle">Data privacy and security practices declared by developer</string>
|
||||||
<string name="details_privacy">"Privacy"</string>
|
<string name="details_privacy">"Privacy"</string>
|
||||||
<string name="details_ratings">"Rating and reviews"</string>
|
<string name="details_ratings">"Rating and reviews"</string>
|
||||||
<string name="details_ratings_title_hint">"Review title"</string>
|
<string name="details_ratings_title_hint">"Review title"</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user