diff --git a/app/build.gradle.kts b/app/build.gradle.kts index db90a3da1..64047bbc1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,7 +29,8 @@ android { buildTypes { release { - isMinifyEnabled = false + isMinifyEnabled = true + isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" @@ -44,6 +45,11 @@ android { compose = true buildConfig = true } + packaging { + jniLibs { + useLegacyPackaging = true + } + } } dependencies { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb4348..ef42cdef8 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -18,4 +18,21 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile + +# Gson rules +-keep class com.google.gson.** { *; } +-keepattributes Signature +-keepattributes *Annotation* + +# Shizuku rules +-keep class rikka.shizuku.** { *; } +-keep class dev.rikka.shizuku.** { *; } + +# Kotlin Reflect +-keep class kotlin.reflect.** { *; } +-keep class com.sameerasw.essentials.domain.model.** { *; } + +# Prevent over-minification of settings and registry classes +-keep class com.sameerasw.essentials.data.repository.** { *; } +-keep class com.sameerasw.essentials.domain.registry.** { *; } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fff3f3096..d0b233fe8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -98,6 +98,15 @@ android:label="@string/tab_app_updates_title" android:theme="@style/Theme.Essentials"> + + { + var showFreezeMenu by remember { mutableStateOf(false) } + Box { + IconButton( + onClick = { + HapticUtil.performVirtualKeyHaptic(view) + showFreezeMenu = true + }, + colors = IconButtonDefaults.iconButtonColors( + containerColor = MaterialTheme.colorScheme.surfaceBright + ) + ) { + Icon( + painter = painterResource(id = R.drawable.rounded_mode_cool_24), + contentDescription = stringResource(R.string.tab_freeze) + ) + } + + SegmentedDropdownMenu( + expanded = showFreezeMenu, + onDismissRequest = { showFreezeMenu = false } + ) { + SegmentedDropdownMenuItem( + text = { Text(stringResource(R.string.action_freeze_all)) }, + onClick = { + showFreezeMenu = false + viewModel.freezeAllApps(context) + }, + leadingIcon = { + Icon( + painter = painterResource(id = R.drawable.rounded_mode_cool_24), + contentDescription = null + ) + } + ) + SegmentedDropdownMenuItem( + text = { Text(stringResource(R.string.action_unfreeze_all)) }, + onClick = { + showFreezeMenu = false + viewModel.unfreezeAllApps(context) + }, + leadingIcon = { + Icon( + painter = painterResource(id = R.drawable.rounded_mode_cool_off_24), + contentDescription = null + ) + } + ) + SegmentedDropdownMenuItem( + text = { Text("Freeze Automatic") }, + onClick = { + showFreezeMenu = false + viewModel.freezeAutomaticApps(context) + }, + leadingIcon = { + Icon( + painter = painterResource(id = R.drawable.rounded_nest_farsight_cool_24), + contentDescription = null + ) + } + ) + } + } + Spacer(modifier = Modifier.width(8.dp)) + } + + DIYTabs.DIY -> { + IconButton( + onClick = { + HapticUtil.performVirtualKeyHaptic(view) + showNewAutomationSheet = true + }, + colors = IconButtonDefaults.iconButtonColors( + containerColor = MaterialTheme.colorScheme.surfaceBright ) - } else { + ) { + Icon( + painter = painterResource(id = R.drawable.rounded_add_24), + contentDescription = stringResource(R.string.diy_editor_new_title) + ) + } + Spacer(modifier = Modifier.width(8.dp)) + } + + DIYTabs.APPS -> { + IconButton( + onClick = { + HapticUtil.performVirtualKeyHaptic(view) + showAddRepoSheet = true + }, + colors = IconButtonDefaults.iconButtonColors( + containerColor = MaterialTheme.colorScheme.surfaceBright + ) + ) { Icon( - painter = painterResource(id = R.drawable.rounded_refresh_24), - contentDescription = stringResource(R.string.action_refresh), - modifier = Modifier.size(24.dp) + painter = painterResource(id = R.drawable.rounded_add_24), + contentDescription = stringResource(R.string.action_add_repo) ) } + Spacer(modifier = Modifier.width(8.dp)) + + IconButton( + onClick = { + HapticUtil.performVirtualKeyHaptic(view) + updatesViewModel.checkForUpdates(context) + }, + enabled = refreshingRepoIds.isEmpty(), + colors = IconButtonDefaults.iconButtonColors( + containerColor = MaterialTheme.colorScheme.surfaceBright + ), + modifier = Modifier.size(40.dp) + ) { + if (refreshingRepoIds.isNotEmpty()) { + CircularWavyProgressIndicator( + progress = { animatedProgress }, + modifier = Modifier.size(24.dp) + ) + } else { + Icon( + painter = painterResource(id = R.drawable.rounded_refresh_24), + contentDescription = stringResource(R.string.action_refresh), + modifier = Modifier.size(24.dp) + ) + } + } + Spacer(modifier = Modifier.width(8.dp)) } - Spacer(modifier = Modifier.width(8.dp)) + + else -> {} } }, hasUpdateAvailable = isUpdateAvailable, @@ -545,7 +652,9 @@ class MainActivity : FragmentActivity() { DIYTabs.DIY -> { DIYScreen( - modifier = Modifier.padding(innerPadding) + modifier = Modifier.padding(innerPadding), + showNewAutomationSheet = showNewAutomationSheet, + onDismissNewAutomationSheet = { showNewAutomationSheet = false } ) } @@ -816,29 +925,9 @@ class MainActivity : FragmentActivity() { } } - // FAB - FloatingActionButton( - onClick = { - HapticUtil.performMediumHaptic(view) - showAddRepoSheet = true - }, - modifier = Modifier - .align(Alignment.BottomEnd) - .padding( - bottom = 150.dp, - end = 32.dp - ), - containerColor = MaterialTheme.colorScheme.primaryContainer, - contentColor = MaterialTheme.colorScheme.onPrimaryContainer - ) { - Icon( - painter = painterResource(id = R.drawable.rounded_add_24), - contentDescription = stringResource(R.string.action_add_repo) - ) } } } - } } } } diff --git a/app/src/main/java/com/sameerasw/essentials/services/tiles/ColorPickerTileService.kt b/app/src/main/java/com/sameerasw/essentials/services/tiles/ColorPickerTileService.kt index 468817d66..bdb675c75 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/tiles/ColorPickerTileService.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/tiles/ColorPickerTileService.kt @@ -29,8 +29,7 @@ class ColorPickerTileService : BaseTileService() { override fun getTileState(): Int = Tile.STATE_INACTIVE override fun onTileClick() { - val intent = Intent(Intent.ACTION_MAIN).apply { - component = ComponentName("com.android.eyedropper", "com.android.eyedropper.MainActivity") + val intent = Intent(this, com.sameerasw.essentials.ui.activities.ColorPickerActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP } diff --git a/app/src/main/java/com/sameerasw/essentials/ui/activities/ColorPickerActivity.kt b/app/src/main/java/com/sameerasw/essentials/ui/activities/ColorPickerActivity.kt new file mode 100644 index 000000000..220564f53 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/ui/activities/ColorPickerActivity.kt @@ -0,0 +1,81 @@ +package com.sameerasw.essentials.ui.activities + +import android.app.Activity +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.os.Build +import android.os.Bundle +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import com.sameerasw.essentials.R +import com.sameerasw.essentials.ui.components.sheets.ColorPickerBottomSheet +import com.sameerasw.essentials.ui.theme.EssentialsTheme +import com.sameerasw.essentials.utils.HapticUtil + +class ColorPickerActivity : ComponentActivity() { + + private var pickedColor by mutableStateOf(null) + private var showBottomSheet by mutableStateOf(false) + + private val eyeDropperLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + val color = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + result.data?.getIntExtra("android.intent.extra.COLOR", Color.BLACK) ?: Color.BLACK + } else { + Color.BLACK + } + + pickedColor = color + showBottomSheet = true + HapticUtil.performHapticForService(this) + } else { + // If they cancelled the eye dropper and no bottom sheet is showing, finish + if (!showBottomSheet) { + finish() + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + EssentialsTheme { + if (showBottomSheet && pickedColor != null) { + ColorPickerBottomSheet( + colorInt = pickedColor!!, + onRetake = { + showBottomSheet = false + launchColorPicker() + }, + onDismissRequest = { + finish() + } + ) + } + } + } + + if (savedInstanceState == null) { + launchColorPicker() + } + } + + private fun launchColorPicker() { + try { + val intent = Intent("android.intent.action.OPEN_EYE_DROPPER") + eyeDropperLauncher.launch(intent) + } catch (e: Exception) { + Toast.makeText(this, getString(R.string.toast_eyedropper_failed), Toast.LENGTH_SHORT).show() + finish() + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/ColorPickerBottomSheet.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/ColorPickerBottomSheet.kt new file mode 100644 index 000000000..564d91e58 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/sheets/ColorPickerBottomSheet.kt @@ -0,0 +1,195 @@ +package com.sameerasw.essentials.ui.components.sheets + +import android.content.Intent +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialShapes +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.material3.toShape +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.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.drawscope.ContentDrawScope +import androidx.compose.ui.graphics.drawscope.drawIntoCanvas +import androidx.compose.ui.graphics.nativeCanvas +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.sameerasw.essentials.R +import com.sameerasw.essentials.ui.components.containers.RoundedCardContainer +import com.sameerasw.essentials.ui.components.pickers.SegmentedPicker +import com.sameerasw.essentials.utils.ColorFormatUtils +import com.sameerasw.essentials.utils.HapticUtil +import kotlin.math.cos +import kotlin.math.sin + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun ColorPickerBottomSheet( + colorInt: Int, + onRetake: () -> Unit, + onDismissRequest: () -> Unit +) { + val context = LocalContext.current + val view = LocalView.current + val clipboardManager = LocalClipboardManager.current + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + + var selectedFormat by remember { mutableStateOf(ColorFormatUtils.ColorFormat.HEX) } + val formattedColor = remember(colorInt, selectedFormat) { + ColorFormatUtils.formatColor(colorInt, selectedFormat) + } + + ModalBottomSheet( + onDismissRequest = onDismissRequest, + sheetState = sheetState, + containerColor = MaterialTheme.colorScheme.surfaceContainerHigh + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + .padding(bottom = 32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(24.dp) + ) { + // Shape + Box( + modifier = Modifier + .size(160.dp) + .clip(MaterialShapes.Cookie6Sided.toShape()) + .background(Color(colorInt)) + .border(5.dp, MaterialTheme.colorScheme.outline, MaterialShapes.Cookie6Sided.toShape()), + contentAlignment = Alignment.Center + ) { + Icon( + painter = painterResource(id = R.drawable.rounded_palette_24), + contentDescription = null, + modifier = Modifier.size(48.dp), + tint = MaterialTheme.colorScheme.outline + ) + } + + RoundedCardContainer { + // Segmented Picker for Format + SegmentedPicker( + items = ColorFormatUtils.ColorFormat.values().toList(), + selectedItem = selectedFormat, + onItemSelected = { selectedFormat = it }, + labelProvider = { it.name }, + modifier = Modifier.fillMaxWidth() + ) + + // Formatted Color Text + Surface( + color = MaterialTheme.colorScheme.surfaceBright, + shape = MaterialTheme.shapes.extraSmall, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = formattedColor, + style = MaterialTheme.typography.headlineMedium, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(16.dp), + textAlign = androidx.compose.ui.text.style.TextAlign.Center + ) + } + } + + // Actions Row + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Retake Button + OutlinedButton( + onClick = { + HapticUtil.performMediumHaptic(view) + onRetake() + }, + modifier = Modifier.weight(0.75f).height(56.dp) + ) { + Icon( + painter = painterResource(id = R.drawable.rounded_colorize_24), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + } + + // Share Button + OutlinedButton( + onClick = { + HapticUtil.performUIHaptic(view) + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, formattedColor) + type = "text/plain" + } + val shareIntent = Intent.createChooser(sendIntent, null) + context.startActivity(shareIntent) + }, + modifier = Modifier.weight(1.5f).height(56.dp) + ) { + Icon( + painter = painterResource(id = R.drawable.rounded_share_24), + contentDescription = "Share", + modifier = Modifier.size(24.dp) + ) + } + + // Copy Button + Button( + onClick = { + HapticUtil.performHeavyHaptic(view) + clipboardManager.setText(AnnotatedString(formattedColor)) + android.widget.Toast.makeText(context, "Copied to clipboard", android.widget.Toast.LENGTH_SHORT).show() + onDismissRequest() + }, + modifier = Modifier.weight(1.5f).height(56.dp) + ) { + Icon( + painter = painterResource(id = R.drawable.rounded_content_copy_24), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + } + } + } + } +} diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/DIYScreen.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/DIYScreen.kt index 3a8ab2cbd..6f8b43cc6 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/DIYScreen.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/DIYScreen.kt @@ -8,23 +8,17 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState 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.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel @@ -33,20 +27,19 @@ import com.sameerasw.essentials.ui.activities.AutomationEditorActivity import com.sameerasw.essentials.ui.components.containers.RoundedCardContainer import com.sameerasw.essentials.ui.components.diy.AutomationItem import com.sameerasw.essentials.ui.components.sheets.NewAutomationSheet -import com.sameerasw.essentials.utils.HapticUtil import com.sameerasw.essentials.viewmodels.DIYViewModel @Composable fun DIYScreen( modifier: Modifier = Modifier, - viewModel: DIYViewModel = viewModel() + viewModel: DIYViewModel = viewModel(), + showNewAutomationSheet: Boolean = false, + onDismissNewAutomationSheet: () -> Unit = {} ) { val context = LocalContext.current val automations by viewModel.automations.collectAsState() val focusManager = LocalFocusManager.current - var showNewAutomationSheet by remember { mutableStateOf(false) } - Box(modifier = modifier.fillMaxSize()) { Column( modifier = Modifier @@ -147,32 +140,13 @@ fun DIYScreen( } } } - - // FAB - val view = LocalView.current - FloatingActionButton( - onClick = { - HapticUtil.performUIHaptic(view) - showNewAutomationSheet = true - }, - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(bottom = 32.dp, end = 32.dp), - containerColor = MaterialTheme.colorScheme.primaryContainer, - contentColor = MaterialTheme.colorScheme.onPrimaryContainer - ) { - Icon( - painter = painterResource(id = R.drawable.rounded_add_24), - contentDescription = stringResource(R.string.diy_editor_new_title) - ) - } } if (showNewAutomationSheet) { NewAutomationSheet( - onDismiss = { showNewAutomationSheet = false }, + onDismiss = onDismissNewAutomationSheet, onOptionSelected = { type -> - showNewAutomationSheet = false + onDismissNewAutomationSheet() context.startActivity(AutomationEditorActivity.createIntent(context, type)) } ) diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/FreezeGridUI.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/FreezeGridUI.kt index d60ff16e9..6dcba1fa5 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/FreezeGridUI.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/FreezeGridUI.kt @@ -1,7 +1,5 @@ package com.sameerasw.essentials.ui.composables -import androidx.activity.compose.BackHandler -import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -25,24 +23,17 @@ import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.FloatingActionButtonMenu -import androidx.compose.material3.FloatingActionButtonMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.LoadingIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text -import androidx.compose.material3.ToggleFloatingActionButton -import androidx.compose.material3.ToggleFloatingActionButtonDefaults.animateIcon import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateMapOf -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -52,13 +43,9 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource -import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.LocalLifecycleOwner @@ -179,19 +166,6 @@ fun FreezeGridUI( } } } - - // FAB - Box( - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(bottom = 150.dp, end = 16.dp) - ) { - ExpandableFreezeFab( - onUnfreezeAll = { viewModel.unfreezeAllApps(context) }, - onFreezeAll = { viewModel.freezeAllApps(context) }, - onFreezeAutomatic = { viewModel.freezeAutomaticApps(context) } - ) - } } } @@ -282,84 +256,3 @@ fun AppGridItem( } } } - -@OptIn(ExperimentalMaterial3ExpressiveApi::class) -@Composable -fun ExpandableFreezeFab( - onUnfreezeAll: () -> Unit, - onFreezeAll: () -> Unit, - onFreezeAutomatic: () -> Unit -) { - var fabMenuExpanded by rememberSaveable { mutableStateOf(false) } - - BackHandler(fabMenuExpanded) { fabMenuExpanded = false } - - FloatingActionButtonMenu( - expanded = fabMenuExpanded, - button = { - ToggleFloatingActionButton( - modifier = Modifier - .semantics { - stateDescription = if (fabMenuExpanded) "Expanded" else "Collapsed" - contentDescription = "Toggle menu" - }, - checked = fabMenuExpanded, - onCheckedChange = { fabMenuExpanded = !fabMenuExpanded }, - ) { - // Animate the icon based on the state - val progress by animateFloatAsState( - targetValue = if (fabMenuExpanded) 1f else 0f, - label = "fab_icon_animation" - ) - - Icon( - painter = painterResource( - id = if (fabMenuExpanded) R.drawable.rounded_close_24 else R.drawable.rounded_mode_cool_24 - ), - contentDescription = null, - modifier = Modifier.animateIcon({ progress }), - ) - } - }, - ) { - FloatingActionButtonMenuItem( - onClick = { - fabMenuExpanded = false - onFreezeAll() - }, - icon = { - Icon( - painterResource(id = R.drawable.rounded_mode_cool_24), - contentDescription = null - ) - }, - text = { Text(text = "Freeze All") }, - ) - FloatingActionButtonMenuItem( - onClick = { - fabMenuExpanded = false - onUnfreezeAll() - }, - icon = { - Icon( - painterResource(id = R.drawable.rounded_mode_cool_off_24), - contentDescription = null - ) - }, - text = { Text(text = "Unfreeze All") }, - ) - FloatingActionButtonMenuItem( - onClick = { - fabMenuExpanded = false - onFreezeAutomatic() - }, - icon = { - Icon( - painterResource(id = R.drawable.rounded_nest_farsight_cool_24), - contentDescription = null - ) - }, - text = { Text(text = "Freeze Automatic") }, - ) - } -} diff --git a/app/src/main/java/com/sameerasw/essentials/utils/ColorFormatUtils.kt b/app/src/main/java/com/sameerasw/essentials/utils/ColorFormatUtils.kt new file mode 100644 index 000000000..942b4dad3 --- /dev/null +++ b/app/src/main/java/com/sameerasw/essentials/utils/ColorFormatUtils.kt @@ -0,0 +1,33 @@ +package com.sameerasw.essentials.utils + +import android.graphics.Color +import androidx.core.graphics.ColorUtils as AndroidColorUtils + +object ColorFormatUtils { + + enum class ColorFormat { + HEX, RGB, HSL, HSV + } + + fun formatColor(color: Int, format: ColorFormat): String { + return when (format) { + ColorFormat.HEX -> String.format("#%06X", 0xFFFFFF and color) + ColorFormat.RGB -> { + val r = Color.red(color) + val g = Color.green(color) + val b = Color.blue(color) + "rgb($r, $g, $b)" + } + ColorFormat.HSL -> { + val hsl = FloatArray(3) + AndroidColorUtils.colorToHSL(color, hsl) + String.format("hsl(%.0f, %.0f%%, %.0f%%)", hsl[0], hsl[1] * 100, hsl[2] * 100) + } + ColorFormat.HSV -> { + val hsv = FloatArray(3) + Color.colorToHSV(color, hsv) + String.format("hsv(%.0f, %.0f%%, %.0f%%)", hsv[0], hsv[1] * 100, hsv[2] * 100) + } + } + } +} diff --git a/app/src/main/res/drawable/rounded_palette_24.xml b/app/src/main/res/drawable/rounded_palette_24.xml new file mode 100644 index 000000000..afe0f1329 --- /dev/null +++ b/app/src/main/res/drawable/rounded_palette_24.xml @@ -0,0 +1,5 @@ + + + + +