mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-26 22:20:20 +00:00
Introduce the ability to change the app icon.
This commit is contained in:
committed by
Greyson Parrelli
parent
7a555d127f
commit
c963e99dca
@@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.appearance
|
||||
|
||||
import android.os.Build
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.Navigation
|
||||
import org.signal.core.util.concurrent.observe
|
||||
@@ -67,6 +68,15 @@ class AppearanceSettingsFragment : DSLSettingsFragment(R.string.preferences__app
|
||||
}
|
||||
)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__app_icon),
|
||||
onClick = {
|
||||
Navigation.findNavController(requireView()).safeNavigate(R.id.action_appearanceSettings_to_appIconActivity)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
radioListPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_chats__message_text_size),
|
||||
listItems = messageFontSizeLabels,
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.appearance.appicon
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.signal.core.ui.Scaffolds
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.settings.app.appearance.appicon.util.AppIconPreset
|
||||
import org.thoughtcrime.securesms.components.settings.app.appearance.appicon.util.AppIconUtility
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
class AppIconSelectionFragment : ComposeFragment() {
|
||||
private lateinit var appIconUtility: AppIconUtility
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
appIconUtility = AppIconUtility(context)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun FragmentContent() {
|
||||
Scaffolds.Settings(
|
||||
title = stringResource(id = R.string.preferences__app_icon),
|
||||
onNavigationClick = {
|
||||
findNavController().popBackStack()
|
||||
},
|
||||
navigationIconPainter = painterResource(id = R.drawable.ic_arrow_left_24),
|
||||
navigationContentDescription = stringResource(id = R.string.Material3SearchToolbar__close)
|
||||
) { contentPadding: PaddingValues ->
|
||||
IconSelectionScreen(appIconUtility.currentAppIcon, ::updateAppIcon, ::openLearnMore, Modifier.padding(contentPadding))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAppIcon(preset: AppIconPreset) {
|
||||
if (!appIconUtility.isCurrentlySelected(preset)) {
|
||||
appIconUtility.setNewAppIcon(preset)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openLearnMore() {
|
||||
findNavController().safeNavigate(R.id.action_appIconSelectionFragment_to_appIconTutorialFragment)
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen allowing the user to view all the possible icon and select a new one to use.
|
||||
*/
|
||||
@Composable
|
||||
fun IconSelectionScreen(activeIcon: AppIconPreset, onItemConfirmed: (AppIconPreset) -> Unit, onWarningClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
var showDialog: Boolean by remember { mutableStateOf(false) }
|
||||
var pendingIcon: AppIconPreset by remember {
|
||||
mutableStateOf(activeIcon)
|
||||
}
|
||||
|
||||
if (showDialog) {
|
||||
ChangeIconDialog(
|
||||
pendingIcon = pendingIcon,
|
||||
onConfirm = {
|
||||
onItemConfirmed(pendingIcon)
|
||||
showDialog = false
|
||||
},
|
||||
onDismiss = {
|
||||
pendingIcon = activeIcon
|
||||
showDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Column(modifier = modifier.verticalScroll(rememberScrollState())) {
|
||||
Spacer(modifier = Modifier.size(12.dp))
|
||||
CaveatWarning(
|
||||
onClick = onWarningClick,
|
||||
modifier = Modifier.padding(horizontal = 24.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.size(12.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 18.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
enumValues<AppIconPreset>().toList().chunked(COLUMN_COUNT).map { it.toImmutableList() }.forEach { items ->
|
||||
IconRow(
|
||||
presets = items,
|
||||
isSelected = { it == pendingIcon },
|
||||
onItemClick = {
|
||||
pendingIcon = it
|
||||
showDialog = true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChangeIconDialog(pendingIcon: AppIconPreset, onConfirm: () -> Unit, onDismiss: () -> Unit, modifier: Modifier = Modifier) {
|
||||
AlertDialog(
|
||||
modifier = modifier,
|
||||
onDismissRequest = onDismiss,
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = onConfirm
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.preferences__app_icon_dialog_ok))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = onDismiss
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.preferences__app_icon_dialog_cancel))
|
||||
}
|
||||
},
|
||||
icon = {
|
||||
AppIcon(preset = pendingIcon, isSelected = false, onClick = {})
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.preferences__app_icon_dialog_title, stringResource(id = pendingIcon.labelResId)),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.preferences__app_icon_dialog_description),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable rendering the one row of icons that the user may choose from.
|
||||
*/
|
||||
@Composable
|
||||
fun IconRow(presets: ImmutableList<AppIconPreset>, isSelected: (AppIconPreset) -> Boolean, onItemClick: (AppIconPreset) -> Unit, modifier: Modifier = Modifier) {
|
||||
Row(modifier = modifier.fillMaxWidth()) {
|
||||
presets.forEach { preset ->
|
||||
val currentlySelected = isSelected(preset)
|
||||
IconGridElement(
|
||||
preset = preset,
|
||||
isSelected = currentlySelected,
|
||||
onClickHandler = {
|
||||
if (!currentlySelected) {
|
||||
onItemClick(preset)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(vertical = 18.dp)
|
||||
.weight(1f)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable rendering an individual icon inside that grid, including the black border of the selected icon.
|
||||
*/
|
||||
@Composable
|
||||
fun IconGridElement(preset: AppIconPreset, isSelected: Boolean, onClickHandler: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
val boxModifier = Modifier.size(64.dp)
|
||||
Box(
|
||||
modifier = if (isSelected) boxModifier.border(3.dp, MaterialTheme.colorScheme.onBackground, CircleShape) else boxModifier
|
||||
) {
|
||||
AppIcon(preset = preset, isSelected = isSelected, onClickHandler, modifier = Modifier.align(Alignment.Center))
|
||||
}
|
||||
Spacer(modifier = Modifier.size(8.dp))
|
||||
Text(
|
||||
text = stringResource(id = preset.labelResId),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable rendering the multiple layers of an adaptive icon onto one flattened rasterized Canvas.
|
||||
*/
|
||||
@Composable
|
||||
fun AppIcon(preset: AppIconPreset, isSelected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val bitmapSize: Dp = if (isSelected) 48.dp else 64.dp
|
||||
val imageModifier = modifier
|
||||
.size(bitmapSize)
|
||||
.graphicsLayer(
|
||||
shape = CircleShape,
|
||||
shadowElevation = if (isSelected) 4f else 8f,
|
||||
clip = true
|
||||
)
|
||||
.clickable(onClick = onClick)
|
||||
Image(
|
||||
painterResource(id = preset.iconPreviewResId),
|
||||
contentDescription = stringResource(id = preset.labelResId),
|
||||
modifier = imageModifier
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A clickable "learn more" block of text.
|
||||
*/
|
||||
@Composable
|
||||
fun CaveatWarning(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val learnMoreString = stringResource(R.string.preferences__app_icon_learn_more)
|
||||
val completeString = stringResource(R.string.preferences__app_icon_warning_learn_more)
|
||||
val learnMoreStartIndex = completeString.indexOf(learnMoreString).coerceAtLeast(0)
|
||||
val learnMoreEndIndex = learnMoreStartIndex + learnMoreString.length
|
||||
val doesStringEndWithLearnMore = learnMoreEndIndex >= completeString.lastIndex
|
||||
val annotatedText = buildAnnotatedString {
|
||||
withStyle(
|
||||
style = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
append(completeString.substring(0, learnMoreStartIndex))
|
||||
}
|
||||
pushStringAnnotation(
|
||||
tag = URL_TAG,
|
||||
annotation = LEARN_MORE_TAG
|
||||
)
|
||||
withStyle(
|
||||
style = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
) {
|
||||
append(learnMoreString)
|
||||
}
|
||||
pop()
|
||||
if (!doesStringEndWithLearnMore) {
|
||||
append(completeString.substring(learnMoreEndIndex, completeString.lastIndex))
|
||||
}
|
||||
}
|
||||
ClickableText(
|
||||
text = annotatedText,
|
||||
onClick = { _ ->
|
||||
onClick()
|
||||
},
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun MainScreenPreview() {
|
||||
IconSelectionScreen(AppIconPreset.DEFAULT, onItemConfirmed = {}, onWarningClick = {})
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = Log.tag(AppIconSelectionFragment::class.java)
|
||||
|
||||
private const val LEARN_MORE_TAG = "learn_more"
|
||||
private const val URL_TAG = "URL"
|
||||
private const val COLUMN_COUNT = 4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.appearance.appicon
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.signal.core.ui.Scaffolds
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
|
||||
class AppIconTutorialFragment : ComposeFragment() {
|
||||
|
||||
@Composable
|
||||
override fun FragmentContent() {
|
||||
Scaffolds.Settings(
|
||||
title = "",
|
||||
onNavigationClick = {
|
||||
findNavController().popBackStack()
|
||||
},
|
||||
navigationIconPainter = painterResource(id = R.drawable.ic_arrow_left_24),
|
||||
navigationContentDescription = stringResource(id = R.string.Material3SearchToolbar__close)
|
||||
) { contentPadding: PaddingValues ->
|
||||
TutorialScreen(Modifier.padding(contentPadding))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TutorialScreen(modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
.align(Alignment.Center)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
val borderShape = RoundedCornerShape(12.dp)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.preferences__app_icon_warning),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(vertical = 20.dp)
|
||||
)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(borderShape)
|
||||
.border(1.dp, MaterialTheme.colorScheme.outline, shape = borderShape)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.app_icon_tutorial_apps_homescreen),
|
||||
contentDescription = stringResource(R.string.preferences__graphic_illustrating_where_the_replacement_app_icon_will_be_visible),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier
|
||||
.widthIn(max = 328.dp)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(id = R.string.preferences__app_icon_notification_warning),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(vertical = 20.dp)
|
||||
)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(borderShape)
|
||||
.border(1.dp, MaterialTheme.colorScheme.outline, shape = borderShape)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.app_icon_tutorial_notification),
|
||||
contentDescription = stringResource(R.string.preferences__graphic_illustrating_where_the_replacement_app_icon_will_be_visible),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier
|
||||
.widthIn(max = 328.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TutorialScreenPreview() {
|
||||
TutorialScreen()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = Log.tag(AppIconTutorialFragment::class.java)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.appearance.appicon.util
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import org.thoughtcrime.securesms.R
|
||||
|
||||
enum class AppIconPreset(private val componentName: String, @DrawableRes val iconPreviewResId: Int, @StringRes val labelResId: Int) {
|
||||
DEFAULT(".RoutingActivity", R.drawable.ic_app_icon_default_top_preview, R.string.app_name),
|
||||
WHITE(".RoutingActivityAltWhite", R.drawable.ic_app_icon_signal_white_top_preview, R.string.app_name),
|
||||
COLOR(".RoutingActivityAltColor", R.drawable.ic_app_icon_signal_color_top_preview, R.string.app_name),
|
||||
DARK(".RoutingActivityAltDark", R.drawable.ic_app_icon_signal_dark_top_preview, R.string.app_name),
|
||||
DARK_VARIANT(".RoutingActivityAltDarkVariant", R.drawable.ic_app_icon_signal_dark_variant_top_preview, R.string.app_name),
|
||||
CHAT(".RoutingActivityAltChat", R.drawable.ic_app_icon_chat_top_preview, R.string.app_name),
|
||||
BUBBLES(".RoutingActivityAltBubbles", R.drawable.ic_app_icon_bubbles_top_preview, R.string.app_name),
|
||||
YELLOW(".RoutingActivityAltYellow", R.drawable.ic_app_icon_yellow_top_preview, R.string.app_name),
|
||||
NEWS(".RoutingActivityAltNews", R.drawable.ic_app_icon_news_top_preview, R.string.app_icon_label_news),
|
||||
NOTES(".RoutingActivityAltNotes", R.drawable.ic_app_icon_notes_top_preview, R.string.app_icon_label_notes),
|
||||
WEATHER(".RoutingActivityAltWeather", R.drawable.ic_app_icon_weather_top_preview, R.string.app_icon_label_weather),
|
||||
WAVES(".RoutingActivityAltWaves", R.drawable.ic_app_icon_waves_top_preview, R.string.app_icon_label_waves);
|
||||
|
||||
fun getComponentName(context: Context): ComponentName {
|
||||
val applicationContext = context.applicationContext
|
||||
return ComponentName(applicationContext, applicationContext.packageName + componentName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.appearance.appicon.util
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import org.signal.core.util.logging.Log
|
||||
|
||||
class AppIconUtility(context: Context) {
|
||||
private val applicationContext: Context = context.applicationContext
|
||||
private val pm = applicationContext.packageManager
|
||||
|
||||
val currentAppIcon by lazy { readCurrentAppIconFromPackageManager() }
|
||||
|
||||
fun isCurrentlySelected(preset: AppIconPreset): Boolean {
|
||||
return preset == currentAppIcon
|
||||
}
|
||||
|
||||
fun currentAppIconComponentName(): ComponentName {
|
||||
return currentAppIcon.getComponentName(applicationContext) ?: AppIconPreset.DEFAULT.getComponentName(applicationContext)
|
||||
}
|
||||
|
||||
fun setNewAppIcon(desiredAppIcon: AppIconPreset) {
|
||||
currentAppIcon.let {
|
||||
pm.setComponentEnabledSetting(it.getComponentName(applicationContext), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
|
||||
}
|
||||
pm.setComponentEnabledSetting(desiredAppIcon.getComponentName(applicationContext), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
|
||||
}
|
||||
|
||||
private fun readCurrentAppIconFromPackageManager(): AppIconPreset {
|
||||
val activeIcon = enumValues<AppIconPreset>().firstOrNull {
|
||||
val componentName = it.getComponentName(applicationContext)
|
||||
val componentEnabledSetting = pm.getComponentEnabledSetting(componentName)
|
||||
|
||||
Log.d(TAG, "Found $componentName with state of $componentEnabledSetting")
|
||||
if (it == AppIconPreset.DEFAULT && componentEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
|
||||
return it
|
||||
}
|
||||
|
||||
componentEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||
}
|
||||
|
||||
return if (activeIcon == null) {
|
||||
setNewAppIcon(AppIconPreset.DEFAULT)
|
||||
AppIconPreset.DEFAULT
|
||||
} else {
|
||||
activeIcon
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "AppIconUtility"
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import com.google.common.collect.Sets;
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.settings.app.appearance.appicon.util.AppIconUtility;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents;
|
||||
import org.thoughtcrime.securesms.database.GroupTable;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
@@ -173,8 +174,10 @@ public final class ConversationUtil {
|
||||
|
||||
List<ShortcutInfoCompat> shortcuts = new ArrayList<>(rankedRecipients.size());
|
||||
|
||||
ComponentName activityName = new AppIconUtility(context).currentAppIconComponentName();
|
||||
|
||||
for (int i = 0; i < rankedRecipients.size(); i++) {
|
||||
ShortcutInfoCompat info = buildShortcutInfo(context, rankedRecipients.get(i), i, Direction.NONE);
|
||||
ShortcutInfoCompat info = buildShortcutInfo(context, activityName, rankedRecipients.get(i), i, Direction.NONE);
|
||||
shortcuts.add(info);
|
||||
}
|
||||
|
||||
@@ -188,7 +191,10 @@ public final class ConversationUtil {
|
||||
*/
|
||||
@WorkerThread
|
||||
private static boolean pushShortcutForRecipientInternal(@NonNull Context context, @NonNull Recipient recipient, int rank, @NonNull Direction direction) {
|
||||
ShortcutInfoCompat shortcutInfo = buildShortcutInfo(context, recipient, rank, direction);
|
||||
|
||||
ComponentName activityName = new AppIconUtility(context).currentAppIconComponentName();
|
||||
|
||||
ShortcutInfoCompat shortcutInfo = buildShortcutInfo(context, activityName, recipient, rank, direction);
|
||||
|
||||
return ShortcutManagerCompat.pushDynamicShortcut(context, shortcutInfo);
|
||||
}
|
||||
@@ -203,6 +209,7 @@ public final class ConversationUtil {
|
||||
*/
|
||||
@WorkerThread
|
||||
private static @NonNull ShortcutInfoCompat buildShortcutInfo(@NonNull Context context,
|
||||
@NonNull ComponentName activity,
|
||||
@NonNull Recipient recipient,
|
||||
int rank,
|
||||
@NonNull Direction direction)
|
||||
@@ -222,7 +229,7 @@ public final class ConversationUtil {
|
||||
.setIcon(AvatarUtil.getIconCompatForShortcut(context, resolved))
|
||||
.setPersons(persons)
|
||||
.setCategories(Sets.newHashSet(CATEGORY_SHARE_TARGET))
|
||||
.setActivity(new ComponentName(context, "org.thoughtcrime.securesms.RoutingActivity"))
|
||||
.setActivity(activity)
|
||||
.setRank(rank)
|
||||
.setLocusId(new LocusIdCompat(shortcutId));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user