diff --git a/app/build.gradle b/app/build.gradle index 2d75706e..399da2d6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,8 +1,4 @@ -plugins { - id 'com.android.application' - id 'com.github.triplet.play' version '3.1.0' -} - +apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: "androidx.navigation.safeargs.kotlin" @@ -11,6 +7,7 @@ apply plugin: 'com.google.gms.google-services' apply plugin: 'dagger.hilt.android.plugin' apply plugin: 'com.mikepenz.aboutlibraries.plugin' apply plugin: 'com.google.firebase.firebase-perf' +apply plugin: 'com.github.triplet.play' android { compileSdkVersion Config.compile_sdk @@ -156,6 +153,9 @@ dependencies { // Android Material implementation Libs.material_components + // Color Picker + implementation "com.github.dhaval2404:colorpicker:2.0" + // Dagger implementation Libs.daggerHiltAndroid kapt Libs.daggerHiltAndroidCompiler @@ -200,7 +200,6 @@ kapt { correctErrorTypes true } -import com.github.triplet.gradle.androidpublisher.ResolutionStrategy play { def serviceAccountFileName = "google-play-api.json" if (rootProject.file(serviceAccountFileName).exists()) { @@ -208,6 +207,5 @@ play { } else if (System.getenv("ANDROID_PUBLISHER_CREDENTIALS") == null) { enabled.set(false) } - resolutionStrategy.set(ResolutionStrategy.IGNORE) releaseName.set(project.version) } \ No newline at end of file diff --git a/app/src/main/java/de/psdev/devdrawer/appwidget/DDWidgetProvider.kt b/app/src/main/java/de/psdev/devdrawer/appwidget/DDWidgetProvider.kt index 09fab4a5..5e5e9bf7 100644 --- a/app/src/main/java/de/psdev/devdrawer/appwidget/DDWidgetProvider.kt +++ b/app/src/main/java/de/psdev/devdrawer/appwidget/DDWidgetProvider.kt @@ -105,7 +105,7 @@ class DDWidgetProvider : AppWidgetProvider() { // Setup the widget, and data source / adapter val widgetView = RemoteViews(context.packageName, R.layout.widget_layout) val widgetColor = widget.color - val contrastColor = getTextColor(widgetColor) + val contrastColor = widgetColor.textColorForBackground() // Set background color for widget widgetView.setInt(R.id.container_actions, "setBackgroundColor", widgetColor) @@ -147,8 +147,4 @@ class DDWidgetProvider : AppWidgetProvider() { return widgetView } - private fun getTextColor(color: Int): Int = when (color) { - Color.RED -> Color.WHITE - else -> textColorForBackground(color) - } } \ No newline at end of file diff --git a/app/src/main/java/de/psdev/devdrawer/profiles/FilterPreviewBottomSheetDialogFragment.kt b/app/src/main/java/de/psdev/devdrawer/profiles/FilterPreviewBottomSheetDialogFragment.kt index ce32cf27..028f61c2 100644 --- a/app/src/main/java/de/psdev/devdrawer/profiles/FilterPreviewBottomSheetDialogFragment.kt +++ b/app/src/main/java/de/psdev/devdrawer/profiles/FilterPreviewBottomSheetDialogFragment.kt @@ -49,6 +49,7 @@ class FilterPreviewBottomSheetDialogFragment : BottomSheetDialogFragment() { _binding = it }.root + @Suppress("DEPRECATION") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) with(binding) { @@ -57,17 +58,17 @@ class FilterPreviewBottomSheetDialogFragment : BottomSheetDialogFragment() { lifecycleScope.launchWhenResumed { withContext(Dispatchers.IO) { val filter = devDrawerDatabase.packageFilterDao().findById(navArgs.packageFilterId) - ?: throw IllegalArgumentException("Unknown filter") + ?: throw IllegalArgumentException("Unknown filter") val context = requireContext() val packageManager = context.packageManager val affectedApps = Firebase.performance.trace("profile_filter_preview") { packageManager.getInstalledPackages(PackageManager.GET_SIGNATURES) - .asSequence() - .map { it.toPackageHashInfo() } - .filter { filter.matches(it) } - .mapNotNull { it.toAppInfo(context) } - .sortedBy { it.name } - .toList() + .asSequence() + .map { it.toPackageHashInfo() } + .filter { filter.matches(it) } + .mapNotNull { it.toAppInfo(context) } + .sortedBy { it.name } + .toList() } logger.warn { "Affected apps: $affectedApps" } withContext(Dispatchers.Main) { diff --git a/app/src/main/java/de/psdev/devdrawer/profiles/WidgetProfileEditFragment.kt b/app/src/main/java/de/psdev/devdrawer/profiles/WidgetProfileEditFragment.kt index adaa7c34..2d3d1614 100644 --- a/app/src/main/java/de/psdev/devdrawer/profiles/WidgetProfileEditFragment.kt +++ b/app/src/main/java/de/psdev/devdrawer/profiles/WidgetProfileEditFragment.kt @@ -1,5 +1,6 @@ package de.psdev.devdrawer.profiles +import android.database.sqlite.SQLiteConstraintException import android.os.Bundle import android.view.* import androidx.core.view.isVisible @@ -8,6 +9,7 @@ import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import de.psdev.devdrawer.BaseFragment import de.psdev.devdrawer.R @@ -18,6 +20,7 @@ import de.psdev.devdrawer.receivers.UpdateReceiver import de.psdev.devdrawer.utils.awaitSubmit import de.psdev.devdrawer.utils.consume import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch import mu.KLogging import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.widget.textChanges @@ -35,26 +38,26 @@ class WidgetProfileEditFragment : BaseFragment private val onDeleteClickListener: PackageFilterActionListener = { packageFilter -> MaterialAlertDialogBuilder(requireContext()) - .setTitle("Delete?") - .setNegativeButton("No") { _, _ -> } - .setPositiveButton("Yes") { _, _ -> - lifecycleScope.launchWhenResumed { - devDrawerDatabase.packageFilterDao().deleteById(packageFilter.id) - UpdateReceiver.send(requireContext()) + .setTitle("Delete?") + .setNegativeButton(R.string.no) { _, _ -> } + .setPositiveButton(R.string.yes) { _, _ -> + lifecycleScope.launchWhenResumed { + devDrawerDatabase.packageFilterDao().deleteById(packageFilter.id) + UpdateReceiver.send(requireContext()) + } } - } - .show() + .show() } private val onPreviewFilterClickListener: PackageFilterActionListener = { packageFilter -> findNavController().navigate( - WidgetProfileEditFragmentDirections.openFilterPreviewBottomSheetDialogFragment( - packageFilterId = packageFilter.id - ) + WidgetProfileEditFragmentDirections.openFilterPreviewBottomSheetDialogFragment( + packageFilterId = packageFilter.id + ) ) } private val listAdapter: PackageFilterListAdapter = PackageFilterListAdapter( - onDeleteClickListener = onDeleteClickListener, - onPreviewFilterClickListener = onPreviewFilterClickListener + onDeleteClickListener = onDeleteClickListener, + onPreviewFilterClickListener = onPreviewFilterClickListener ) private var widgetProfile: WidgetProfile? = null @@ -66,9 +69,9 @@ class WidgetProfileEditFragment : BaseFragment } override fun createViewBinding( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? ): FragmentWidgetProfileEditBinding = FragmentWidgetProfileEditBinding.inflate(inflater, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -79,9 +82,9 @@ class WidgetProfileEditFragment : BaseFragment btnAddFilter.setOnClickListener { _ -> widgetProfile?.let { val directions = - WidgetProfileEditFragmentDirections.openAddPackageFilterBottomSheetDialogFragment( - widgetProfileId = it.id - ) + WidgetProfileEditFragmentDirections.openAddPackageFilterBottomSheetDialogFragment( + widgetProfileId = it.id + ) findNavController().navigate(directions) } } @@ -89,9 +92,9 @@ class WidgetProfileEditFragment : BaseFragment btnAddSignature.setOnClickListener { widgetProfile?.let { val directions = - WidgetProfileEditFragmentDirections.openAppSignatureChooserBottomSheetDialogFragment( - widgetProfileId = it.id - ) + WidgetProfileEditFragmentDirections.openAppSignatureChooserBottomSheetDialogFragment( + widgetProfileId = it.id + ) findNavController().navigate(directions) } } @@ -142,18 +145,22 @@ class WidgetProfileEditFragment : BaseFragment override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { R.id.action_delete -> consume { MaterialAlertDialogBuilder(requireContext()) - .setTitle("Delete profile?") - .setNegativeButton("No") { _, _ -> } - .setPositiveButton("Yes") { _, _ -> - widgetProfile?.let { widgetProfile -> - lifecycleScope.launchWhenResumed { - devDrawerDatabase.widgetProfileDao().delete(widgetProfile) - UpdateReceiver.send(requireContext()) - findNavController().popBackStack() + .setTitle("Delete profile?") + .setNegativeButton(R.string.no) { _, _ -> } + .setPositiveButton(R.string.yes) { _, _ -> + widgetProfile?.let { widgetProfile -> + lifecycleScope.launch { + try { + devDrawerDatabase.widgetProfileDao().delete(widgetProfile) + UpdateReceiver.send(requireContext()) + findNavController().popBackStack() + } catch (e: SQLiteConstraintException) { + Snackbar.make(binding.root, R.string.error_profile_in_use, Snackbar.LENGTH_LONG).show() + } + } } } - } - .show() + .show() } else -> super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/de/psdev/devdrawer/profiles/WidgetProfileListFragment.kt b/app/src/main/java/de/psdev/devdrawer/profiles/WidgetProfileListFragment.kt index 09d08d3d..769ee55f 100644 --- a/app/src/main/java/de/psdev/devdrawer/profiles/WidgetProfileListFragment.kt +++ b/app/src/main/java/de/psdev/devdrawer/profiles/WidgetProfileListFragment.kt @@ -117,7 +117,7 @@ class WidgetProfileListFragment: BaseFragment( try { devDrawerDatabase.widgetProfileDao().delete(widgetProfile) } catch (e: SQLiteConstraintException) { - Snackbar.make(binding.root, "Cannot delete profile, still being used by widgets", Snackbar.LENGTH_LONG).show() + Snackbar.make(binding.root, R.string.error_profile_in_use, Snackbar.LENGTH_LONG).show() } } tracker.deselect(selectedProfile) diff --git a/app/src/main/java/de/psdev/devdrawer/utils/Bindings.kt b/app/src/main/java/de/psdev/devdrawer/utils/Bindings.kt index 115488a2..2ff83d2d 100644 --- a/app/src/main/java/de/psdev/devdrawer/utils/Bindings.kt +++ b/app/src/main/java/de/psdev/devdrawer/utils/Bindings.kt @@ -2,7 +2,7 @@ package de.psdev.devdrawer.utils import android.widget.Button import android.widget.EditText -import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -13,5 +13,5 @@ fun MutableStateFlow.receiveTextChangesFrom(editText: EditText) = editTe .map { it.toString() } .onEach { value = it } -fun SendChannel.receiveClicksFrom(button: Button) = button.clicks() - .onEach { offer(it) } +fun MutableSharedFlow.receiveClicksFrom(button: Button) = button.clicks() + .onEach { emit(it) } diff --git a/app/src/main/java/de/psdev/devdrawer/utils/Colors.kt b/app/src/main/java/de/psdev/devdrawer/utils/Colors.kt index 4b200867..d2385907 100644 --- a/app/src/main/java/de/psdev/devdrawer/utils/Colors.kt +++ b/app/src/main/java/de/psdev/devdrawer/utils/Colors.kt @@ -7,26 +7,55 @@ import androidx.core.graphics.blue import androidx.core.graphics.green import androidx.core.graphics.red -fun Int.rgbToYiq(): Int = ((red * 299) + (green * 587) + (blue * 114)) / 1000 - -fun getContrastColor(@ColorInt color: Int): Int { - val whiteContrast = ColorUtils.calculateContrast(Color.WHITE, color) - val blackContrast = ColorUtils.calculateContrast(Color.BLACK, color) - return if (whiteContrast > blackContrast) Color.WHITE else Color.BLACK -} - -fun textColorForBackground(backgroundColor: Int): Int { - val r = backgroundColor.red * 255 - val g = backgroundColor.green * 255 - val b = backgroundColor.blue * 255 +@ColorInt +fun @receiver:ColorInt Int.textColorForBackground(): Int { + val r = red + val g = green + val b = blue val yiq = (r * 299 + g * 587 + b * 114) / 1000 - return if (yiq >= 128) Color.BLACK else Color.WHITE + return if (yiq >= 160) Color.BLACK else Color.WHITE } @ColorInt -fun getDesaturatedColor(@ColorInt color: Int): Int { +fun @receiver:ColorInt Int.getDesaturatedColor(): Int { val result = FloatArray(size = 3) - Color.colorToHSV(color, result) + Color.colorToHSV(this, result) result[1] *= 0.6f return Color.HSVToColor(result) +} + +/** + * from https://medium.com/@anthony.st91/sort-things-by-colors-in-android-f34dc2c9f4b7 + */ +fun @receiver:ColorInt Int.getHSL(): FloatArray { + val hsl = FloatArray(3) + ColorUtils.colorToHSL(this, hsl) + for (i in hsl.indices) { + hsl[i] = hsl[i] * 100 + } + return hsl +} + +/** + * from https://medium.com/@anthony.st91/sort-things-by-colors-in-android-f34dc2c9f4b7 + */ +fun @receiver:ColorInt IntArray.sortColorList(): List = sortedWith { o1, o2 -> + val hsl1 = o1.getHSL() + val hsl2 = o2.getHSL() + + // Colors have the same Hue + if (hsl1[0] == hsl2[0]) { + + // Colors have the same saturation + if (hsl1[1] == hsl2[1]) { + // Compare lightness + (hsl1[2] - hsl2[2]).toInt() + + } else { + (hsl1[1] - hsl2[1]).toInt() + } + + } else { + (hsl1[0] - hsl2[0]).toInt() + } } \ No newline at end of file diff --git a/app/src/main/java/de/psdev/devdrawer/widgets/EditWidgetFragment.kt b/app/src/main/java/de/psdev/devdrawer/widgets/EditWidgetFragment.kt index 9f05d992..2e4e02a7 100644 --- a/app/src/main/java/de/psdev/devdrawer/widgets/EditWidgetFragment.kt +++ b/app/src/main/java/de/psdev/devdrawer/widgets/EditWidgetFragment.kt @@ -3,6 +3,7 @@ package de.psdev.devdrawer.widgets import android.app.Activity import android.appwidget.AppWidgetManager import android.content.Intent +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -16,6 +17,8 @@ import androidx.recyclerview.selection.SelectionPredicates import androidx.recyclerview.selection.SelectionTracker import androidx.recyclerview.selection.StorageStrategy import androidx.recyclerview.widget.LinearLayoutManager +import com.github.dhaval2404.colorpicker.MaterialColorPickerDialog +import com.github.dhaval2404.colorpicker.model.ColorShape import dagger.hilt.android.AndroidEntryPoint import de.psdev.devdrawer.BaseFragment import de.psdev.devdrawer.R @@ -29,8 +32,12 @@ import de.psdev.devdrawer.receivers.UpdateReceiver import de.psdev.devdrawer.utils.awaitSubmit import de.psdev.devdrawer.utils.receiveClicksFrom import de.psdev.devdrawer.utils.receiveTextChangesFrom +import de.psdev.devdrawer.utils.sortColorList import de.psdev.devdrawer.widgets.EditWidgetFragmentViewModel.Selection -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject @@ -61,17 +68,55 @@ class EditWidgetFragment : BaseFragment() { findNavController().navigate(EditWidgetFragmentDirections.createProfileAction(widgetProfile.id)) } // Setup views - binding.editName.setText("Widget ${args.widgetId}") + with(binding) { + with(editName) { + setText("Widget ${args.widgetId}") + } + with(btnColor) { + setOnClickListener { + val currentColor = viewModel.savedWidget.value?.color ?: Color.BLACK + MaterialColorPickerDialog + .Builder(requireContext()) + .setTitle(R.string.pick_widget_color) + .setDefaultColor(currentColor) + .setColorShape(ColorShape.SQAURE) + .setColorRes(resources.getIntArray(R.array.widget_colors).sortColorList()) + .setPositiveButton(R.string.ok) + .setNegativeButton(R.string.cancel) + .setColorListener { color, _ -> + setBackgroundColor(color) + viewModel.inputColor.value = color + } + .showBottomSheet(childFragmentManager) + } + } + } + lifecycleScope.launchWhenResumed { + with(binding) { + val widget = checkNotNull(viewModel.savedWidget.filterNotNull().first()) + editName.setText(widget.name) + btnColor.setBackgroundColor(widget.color) + } + } + + binding.btnNewProfile.setOnClickListener { + lifecycleScope.launchWhenResumed { + val widgetProfile = WidgetProfile(name = "Profile for ${viewModel.inputWidgetName.value}") + devDrawerDatabase.widgetProfileDao().insert(widgetProfile) + findNavController().navigate(EditWidgetFragmentDirections.createProfileAction(widgetProfile.id)) + } + } + binding.recyclerProfiles.layoutManager = LinearLayoutManager(view.context, LinearLayoutManager.VERTICAL, false) binding.recyclerProfiles.adapter = adapter val selectionTracker = SelectionTracker.Builder( - "widgetProfile", - binding.recyclerProfiles, - WidgetProfilesItemKeyProvider(adapter), - WidgetProfilesDetailsLookup(binding.recyclerProfiles), - StorageStrategy.createStringStorage() + "widgetProfile", + binding.recyclerProfiles, + WidgetProfilesItemKeyProvider(adapter), + WidgetProfilesDetailsLookup(binding.recyclerProfiles), + StorageStrategy.createStringStorage() ).withSelectionPredicate( - SelectionPredicates.createSelectSingleAnything() + SelectionPredicates.createSelectSingleAnything() ).build().also { it.onRestoreInstanceState(savedInstanceState) if (savedInstanceState == null) { @@ -83,18 +128,6 @@ class EditWidgetFragment : BaseFragment() { } adapter.selectionTracker = selectionTracker - lifecycleScope.launchWhenResumed { - binding.editName.setText(viewModel.savedWidget.filterNotNull().first().name) - } - - binding.btnNewProfile.setOnClickListener { - lifecycleScope.launchWhenResumed { - val widgetProfile = WidgetProfile(name = "Profile for ${viewModel.inputWidgetName.value}") - devDrawerDatabase.widgetProfileDao().insert(widgetProfile) - findNavController().navigate(EditWidgetFragmentDirections.createProfileAction(widgetProfile.id)) - } - } - viewLifecycleScope.launch { viewModel.inputWidgetName.receiveTextChangesFrom(binding.editName).launchIn(this) @@ -128,7 +161,7 @@ class EditWidgetFragment : BaseFragment() { } } }.launchIn(this) - viewModel.outputCloseTrigger.asFlow().onEach { widget -> + viewModel.outputCloseTrigger.onEach { widget -> val resultValue = Intent().apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget.id) } diff --git a/app/src/main/java/de/psdev/devdrawer/widgets/EditWidgetFragmentViewModel.kt b/app/src/main/java/de/psdev/devdrawer/widgets/EditWidgetFragmentViewModel.kt index a0c578a9..a677903a 100644 --- a/app/src/main/java/de/psdev/devdrawer/widgets/EditWidgetFragmentViewModel.kt +++ b/app/src/main/java/de/psdev/devdrawer/widgets/EditWidgetFragmentViewModel.kt @@ -9,33 +9,26 @@ import androidx.lifecycle.viewModelScope import de.psdev.devdrawer.database.DevDrawerDatabase import de.psdev.devdrawer.database.Widget import de.psdev.devdrawer.database.WidgetProfile -import kotlinx.coroutines.channels.BroadcastChannel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.zip +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import mu.KLogging -import kotlin.random.Random class EditWidgetFragmentViewModel @ViewModelInject constructor( - private val database: DevDrawerDatabase, - @Assisted private val savedStateHandle: SavedStateHandle -): ViewModel() { + private val database: DevDrawerDatabase, + @Assisted private val savedStateHandle: SavedStateHandle +) : ViewModel() { - companion object: KLogging(); + companion object : KLogging() // TODO Replace by safer method once https://github.com/google/dagger/issues/1906 is solved - val widgetId = savedStateHandle.get("widgetId") ?: throw IllegalStateException("No widgetId") + val widgetId = savedStateHandle.get("widgetId") + ?: throw IllegalStateException("No widgetId") // Inputs val inputWidgetName = MutableStateFlow("") + val inputColor = MutableStateFlow(Color.BLACK) val inputSelectedProfile = MutableStateFlow(Selection.Nothing) - val inputSaveTrigger = BroadcastChannel(1) + val inputSaveTrigger = MutableSharedFlow(1) // Outputs val outputWidgetProfiles @@ -46,38 +39,51 @@ class EditWidgetFragmentViewModel @ViewModelInject constructor( } // TODO add sealed class for success / cancel - val outputCloseTrigger = BroadcastChannel(1) + val outputCloseTrigger = MutableSharedFlow(1) val savedWidget: MutableStateFlow = MutableStateFlow(null) init { viewModelScope.launch { - savedWidget.value = database.widgetDao().findById(widgetId) - inputSaveTrigger.asFlow().flatMapLatest { - inputWidgetName.zip(inputSelectedProfile.filterIsInstance()) { name, selection -> - savedWidget.value?.copy( + savedWidget.value = database.widgetDao().findById(widgetId)?.also { widget -> + inputWidgetName.value = widget.name + inputColor.value = widget.color + } + } + combine(inputWidgetName, inputColor) { name, color -> + logger.info { "Update savedWidget: $name, $color" } + savedWidget.value?.let { widget -> + widget.name = name + widget.color = color + } + }.launchIn(viewModelScope) + inputSaveTrigger.asSharedFlow().flatMapLatest { + combine( + inputWidgetName, + inputColor, + inputSelectedProfile.filterIsInstance() + ) { name, color, selection -> + savedWidget.value?.copy( name = name, + color = color, profileId = selection.profile.id - ) ?: Widget(id = widgetId, name = name, color = colors[Random.nextInt(colors.size)], profileId = selection.profile.id) - } - }.onEach { widget -> - database.widgetDao().insertOrUpdate(widget) - savedWidget.value = widget - outputCloseTrigger.offer(widget) - }.launchIn(this) - } + ) ?: Widget( + id = widgetId, + name = name, + color = color, + profileId = selection.profile.id + ) + } + }.onEach { widget -> + database.widgetDao().insertOrUpdate(widget) + savedWidget.value = widget + outputCloseTrigger.emit(widget) + }.launchIn(viewModelScope) } - private val colors = listOf( - Color.RED, - Color.BLACK, - Color.BLUE, - Color.GREEN - ) - sealed class Selection { - object Nothing: Selection() - data class Profile(val profile: WidgetProfile): Selection() + object Nothing : Selection() + data class Profile(val profile: WidgetProfile) : Selection() } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_widget_edit.xml b/app/src/main/res/layout/fragment_widget_edit.xml index 36a733ea..8b8abab3 100644 --- a/app/src/main/res/layout/fragment_widget_edit.xml +++ b/app/src/main/res/layout/fragment_widget_edit.xml @@ -11,13 +11,14 @@ style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="16dp" android:hint="@string/name" + app:counterEnabled="true" + app:counterMaxLength="50" app:endIconMode="clear_text" app:helperText="Choose a name for the widget" app:helperTextEnabled="true" app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toRightOf="parent" + app:layout_constraintRight_toLeftOf="@id/btn_color" app:layout_constraintTop_toTopOf="parent"> + + + #222 #4D8BC34A #000 + + + @color/black + @color/white + @color/amber_400 + @color/blue_400 + @color/blue_grey_400 + @color/brown_400 + @color/cyan_400 + @color/deep_orange_400 + @color/deep_purple_400 + @color/green_400 + @color/grey_400 + @color/indigo_400 + @color/light_blue_400 + @color/light_green_400 + @color/lime_400 + @color/orange_400 + @color/pink_400 + @color/purple_400 + @color/red_400 + @color/teal_400 + @color/yellow_400 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 277a3ccd..e4f04d46 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -64,5 +64,10 @@ Cancel No widgets created Add widget + Pick widget color + OK + Cannot delete profile, still being used by widgets + No + Yes diff --git a/build.gradle b/build.gradle index c5585c8c..451f7531 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,7 @@ buildscript { classpath Plugins.firebasePerformancePlugin classpath Plugins.daggerHiltPlugin classpath Plugins.aboutLibrariesPlugin + classpath "com.github.triplet.gradle:play-publisher:3.1.0" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/gradle.properties b/gradle.properties index 3bf4aa2c..b7d75211 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,12 +10,12 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 - # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true - # Enable Jetifier android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true +# Kotlin +kotlin.code.style=official \ No newline at end of file