From 75529b8f4291968dec36ed5f1efa97d075e3d632 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Thu, 27 Jan 2022 22:22:10 +0100 Subject: [PATCH] Migrate icon shape preference --- .../fragment/PreferencesAppearanceFragment.kt | 32 -- .../res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../src/debug/res/mipmap-hdpi/ic_launcher.png | Bin .../mipmap-hdpi/ic_launcher_foreground.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../src/debug/res/mipmap-mdpi/ic_launcher.png | Bin .../mipmap-mdpi/ic_launcher_foreground.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../debug/res/mipmap-xhdpi/ic_launcher.png | Bin .../mipmap-xhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../debug/res/mipmap-xxhdpi/ic_launcher.png | Bin .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../debug/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../mipmap-hdpi/ic_launcher_foreground.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../mipmap-mdpi/ic_launcher_foreground.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../mipmap-xhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../res/values/ic_launcher_background.xml | 0 .../de/mm20/launcher2/preferences/Defaults.kt | 7 + preferences/src/main/proto/settings.proto | 24 ++ .../search/ApplicationDetailRepresentation.kt | 8 +- .../legacy/search/BasicGridRepresentation.kt | 8 +- .../search/ContactDetailRepresentation.kt | 8 +- .../search/ContactListRepresentation.kt | 8 +- .../legacy/search/FileDetailRepresentation.kt | 7 +- .../legacy/search/FileListRepresentation.kt | 7 +- .../search/ShortcutDetailRepresentation.kt | 7 +- .../search/WebsiteDetailRepresentation.kt | 16 +- .../search/WebsiteListRepresentation.kt | 7 +- .../ui/legacy/view/LauncherIconView.kt | 334 ++++++++++++------ .../appearance/AppearanceSettingsScreen.kt | 137 ++++++- .../appearance/AppearanceSettingsScreenVM.kt | 20 +- 50 files changed, 467 insertions(+), 163 deletions(-) rename {app => base}/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename {app => base}/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename {app => base}/src/debug/res/mipmap-hdpi/ic_launcher.png (100%) rename {app => base}/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png (100%) rename {app => base}/src/debug/res/mipmap-hdpi/ic_launcher_round.png (100%) rename {app => base}/src/debug/res/mipmap-mdpi/ic_launcher.png (100%) rename {app => base}/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png (100%) rename {app => base}/src/debug/res/mipmap-mdpi/ic_launcher_round.png (100%) rename {app => base}/src/debug/res/mipmap-xhdpi/ic_launcher.png (100%) rename {app => base}/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png (100%) rename {app => base}/src/debug/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename {app => base}/src/debug/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {app => base}/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png (100%) rename {app => base}/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename {app => base}/src/debug/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {app => base}/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png (100%) rename {app => base}/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename {app => base}/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename {app => base}/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename {app => base}/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {app => base}/src/main/res/mipmap-hdpi/ic_launcher_foreground.png (100%) rename {app => base}/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename {app => base}/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {app => base}/src/main/res/mipmap-mdpi/ic_launcher_foreground.png (100%) rename {app => base}/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename {app => base}/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {app => base}/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png (100%) rename {app => base}/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename {app => base}/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {app => base}/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png (100%) rename {app => base}/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename {app => base}/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {app => base}/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png (100%) rename {app => base}/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename {app/src/debug => base/src/main}/res/values/ic_launcher_background.xml (100%) diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt index 0f176b80..c4bb8b17 100644 --- a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt @@ -8,7 +8,6 @@ import android.view.View import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.app.AppCompatDelegate import androidx.lifecycle.lifecycleScope import androidx.preference.ListPreference import androidx.preference.Preference @@ -88,7 +87,6 @@ class PreferencesAppearanceFragment : PreferenceFragmentCompat() { } val shapePreference = findPreference("icon_shape")!! - shapePreference.summary = getShapeName() shapePreference.setOnPreferenceClickListener { val launcherIcon = LauncherIcon( foreground = requireContext().getDrawable(R.mipmap.ic_launcher_foreground)!!, @@ -110,23 +108,6 @@ class PreferencesAppearanceFragment : PreferenceFragmentCompat() { LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) val dialog = MaterialDialog(requireContext()) - shapes.forEachIndexed { i, shape -> - val view = View.inflate(requireContext(), R.layout.preference_icon_shape_row, null) - view.findViewById(R.id.icon).also { iconView -> - iconView.icon = launcherIcon - iconView.shape = shape.first - } - view.findViewById(R.id.label).also { labelView -> - labelView.setText(shape.second) - } - view.layoutParams = layoutParams - iconShapeList.addView(view) - view.setOnClickListener { - LauncherPreferences.instance.iconShape = shape.first - shapePreference.summary = getShapeName() - dialog.dismiss() - } - } dialog.customView(view = iconShapeList, scrollable = true) .title(R.string.preference_icon_shape) @@ -143,19 +124,6 @@ class PreferencesAppearanceFragment : PreferenceFragmentCompat() { } } - private fun getShapeName(): String { - return requireContext().getString(when (LauncherIconView.getDefaultShape(requireContext())) { - IconShape.TRIANGLE -> R.string.preference_icon_shape_triangle - IconShape.HEXAGON -> R.string.preference_icon_shape_hexagon - IconShape.ROUNDED_SQUARE -> R.string.preference_icon_shape_rounded_square - IconShape.SQUIRCLE -> R.string.preference_icon_shape_squircle - IconShape.SQUARE -> R.string.preference_icon_shape_square - IconShape.PENTAGON -> R.string.preference_icon_shape_pentagon - IconShape.PLATFORM_DEFAULT -> R.string.preference_icon_shape_platform - else -> R.string.preference_icon_shape_circle - }) - } - override fun onResume() { super.onResume() diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/base/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml rename to base/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/base/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to base/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher.png b/base/src/debug/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from app/src/debug/res/mipmap-hdpi/ic_launcher.png rename to base/src/debug/res/mipmap-hdpi/ic_launcher.png diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png b/base/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png rename to base/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png b/base/src/debug/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from app/src/debug/res/mipmap-hdpi/ic_launcher_round.png rename to base/src/debug/res/mipmap-hdpi/ic_launcher_round.png diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher.png b/base/src/debug/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from app/src/debug/res/mipmap-mdpi/ic_launcher.png rename to base/src/debug/res/mipmap-mdpi/ic_launcher.png diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png b/base/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png rename to base/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png b/base/src/debug/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from app/src/debug/res/mipmap-mdpi/ic_launcher_round.png rename to base/src/debug/res/mipmap-mdpi/ic_launcher_round.png diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png b/base/src/debug/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from app/src/debug/res/mipmap-xhdpi/ic_launcher.png rename to base/src/debug/res/mipmap-xhdpi/ic_launcher.png diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png b/base/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png rename to base/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png b/base/src/debug/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png rename to base/src/debug/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/base/src/debug/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from app/src/debug/res/mipmap-xxhdpi/ic_launcher.png rename to base/src/debug/res/mipmap-xxhdpi/ic_launcher.png diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png b/base/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png rename to base/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/base/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png rename to base/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/base/src/debug/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png rename to base/src/debug/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/base/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png rename to base/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/base/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png rename to base/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/base/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to base/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/base/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to base/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/base/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher.png rename to base/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/base/src/main/res/mipmap-hdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png rename to base/src/main/res/mipmap-hdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/base/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to base/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/base/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher.png rename to base/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/base/src/main/res/mipmap-mdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png rename to base/src/main/res/mipmap-mdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/base/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to base/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/base/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to base/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/base/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png rename to base/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/base/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to base/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/base/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to base/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/base/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png rename to base/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/base/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to base/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/base/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to base/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/base/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png rename to base/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/base/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to base/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/app/src/debug/res/values/ic_launcher_background.xml b/base/src/main/res/values/ic_launcher_background.xml similarity index 100% rename from app/src/debug/res/values/ic_launcher_background.xml rename to base/src/main/res/values/ic_launcher_background.xml diff --git a/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt b/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt index d0e3060f..b679d748 100644 --- a/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt +++ b/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt @@ -106,5 +106,12 @@ fun createFactorySettings(context: Context): Settings { .setSearchBarStyle(Settings.SearchBarSettings.SearchBarStyle.Transparent) .build() ) + .setIcons( + Settings.IconSettings.newBuilder() + .setLegacyIconBg(Settings.IconSettings.LegacyIconBackground.Dynamic) + .setShape(Settings.IconSettings.IconShape.PlatformDefault) + .setThemedIcons(false) + .setIconPack("") + ) .build() } \ No newline at end of file diff --git a/preferences/src/main/proto/settings.proto b/preferences/src/main/proto/settings.proto index 4f17cd3a..89bc132d 100644 --- a/preferences/src/main/proto/settings.proto +++ b/preferences/src/main/proto/settings.proto @@ -133,4 +133,28 @@ message Settings { } SearchBarSettings search_bar = 20; + message IconSettings { + enum IconShape { + PlatformDefault = 0; + Circle = 1; + Square = 2; + RoundedSquare = 3; + Triangle = 4; + Squircle = 5; + Hexagon = 6; + Pentagon = 7; + EasterEgg = 8; + } + IconShape shape = 1; + bool themed_icons = 2; + string icon_pack = 3; + enum LegacyIconBackground { + Dynamic = 0; + None = 1; + White = 2; + } + LegacyIconBackground legacyIconBg = 4; + } + IconSettings icons = 21; + } \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ApplicationDetailRepresentation.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ApplicationDetailRepresentation.kt index 9202e25d..6b309a68 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ApplicationDetailRepresentation.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ApplicationDetailRepresentation.kt @@ -40,7 +40,6 @@ import de.mm20.launcher2.icons.IconRepository import de.mm20.launcher2.ktx.* import de.mm20.launcher2.legacy.helper.ActivityStarter import de.mm20.launcher2.notifications.NotificationRepository -import de.mm20.launcher2.notifications.NotificationService import de.mm20.launcher2.search.data.AppInstallation import de.mm20.launcher2.search.data.Application import de.mm20.launcher2.search.data.LauncherApp @@ -79,8 +78,8 @@ class ApplicationDetailRepresentation : Representation, KoinComponent { setOnLongClickListener(null) findViewById(R.id.appName).text = application.label val iconView = findViewById(R.id.icon).apply { - shape = LauncherIconView.getDefaultShape(context) icon = iconRepository.getIconIfCached(application) + shape = LauncherIconView.currentShape } val notificationView = findViewById(R.id.notifications) @@ -107,6 +106,11 @@ class ApplicationDetailRepresentation : Representation, KoinComponent { updateNotifications(notificationView, it) } } + launch { + LauncherIconView.getDefaultShape().collectLatest { + iconView.shape = it + } + } } } findViewById(R.id.appCard).also { diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/BasicGridRepresentation.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/BasicGridRepresentation.kt index 6de83249..5b3f962a 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/BasicGridRepresentation.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/BasicGridRepresentation.kt @@ -9,7 +9,6 @@ import de.mm20.launcher2.badges.BadgeRepository import de.mm20.launcher2.icons.IconRepository import de.mm20.launcher2.ktx.dp import de.mm20.launcher2.ktx.lifecycleOwner -import de.mm20.launcher2.ktx.lifecycleScope import de.mm20.launcher2.legacy.helper.ActivityStarter import de.mm20.launcher2.search.data.Searchable import de.mm20.launcher2.ui.R @@ -46,7 +45,6 @@ class BasicGridRepresentation : Representation, KoinComponent { .alpha(1f) .start()*/ findViewById(R.id.icon).apply { - shape = LauncherIconView.getDefaultShape(context) setOnClickListener { if (!ActivityStarter.start( context, @@ -58,6 +56,7 @@ class BasicGridRepresentation : Representation, KoinComponent { } } icon = iconRepository.getIconIfCached(searchable) + shape = LauncherIconView.currentShape job = rootView.scope.launch { rootView.lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -72,6 +71,11 @@ class BasicGridRepresentation : Representation, KoinComponent { badge = it } } + launch { + LauncherIconView.getDefaultShape().collectLatest { + shape = it + } + } } } setOnLongClickListener { diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ContactDetailRepresentation.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ContactDetailRepresentation.kt index 22853318..8a48c17e 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ContactDetailRepresentation.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ContactDetailRepresentation.kt @@ -20,7 +20,6 @@ import de.mm20.launcher2.badges.BadgeRepository import de.mm20.launcher2.icons.IconRepository import de.mm20.launcher2.ktx.dp import de.mm20.launcher2.ktx.lifecycleOwner -import de.mm20.launcher2.ktx.lifecycleScope import de.mm20.launcher2.ktx.setStartCompoundDrawable import de.mm20.launcher2.legacy.helper.ActivityStarter import de.mm20.launcher2.search.data.Contact @@ -54,8 +53,8 @@ class ContactDetailRepresentation : Representation, KoinComponent { scene.setEnterAction { with(rootView) { findViewById(R.id.icon).apply { - shape = LauncherIconView.getDefaultShape(context) icon = iconRepository.getIconIfCached(contact) + shape = LauncherIconView.currentShape job = rootView.scope.launch { rootView.lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { launch { @@ -69,6 +68,11 @@ class ContactDetailRepresentation : Representation, KoinComponent { badge = it } } + launch { + LauncherIconView.getDefaultShape().collectLatest { + shape = it + } + } } } } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ContactListRepresentation.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ContactListRepresentation.kt index 7aff3a30..bacb9fce 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ContactListRepresentation.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ContactListRepresentation.kt @@ -10,7 +10,6 @@ import de.mm20.launcher2.ui.R import de.mm20.launcher2.icons.IconRepository import de.mm20.launcher2.ktx.dp import de.mm20.launcher2.ktx.lifecycleOwner -import de.mm20.launcher2.ktx.lifecycleScope import de.mm20.launcher2.search.data.Contact import de.mm20.launcher2.search.data.Searchable import de.mm20.launcher2.ui.legacy.searchable.SearchableView @@ -38,8 +37,8 @@ class ContactListRepresentation : Representation, KoinComponent { scene.setEnterAction { with(rootView) { findViewById(R.id.icon).apply { - shape = LauncherIconView.getDefaultShape(context) icon = iconRepository.getIconIfCached(contact) + shape = LauncherIconView.currentShape job = rootView.scope.launch { rootView.lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { launch { @@ -53,6 +52,11 @@ class ContactListRepresentation : Representation, KoinComponent { badge = it } } + launch { + LauncherIconView.getDefaultShape().collectLatest { + shape = it + } + } } } } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/FileDetailRepresentation.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/FileDetailRepresentation.kt index ee7ed985..581f4765 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/FileDetailRepresentation.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/FileDetailRepresentation.kt @@ -48,8 +48,8 @@ class FileDetailRepresentation : Representation, KoinComponent { findViewById(R.id.fileLabel).text = file.label findViewById(R.id.fileInfo).text = getInfo(context, file) findViewById(R.id.icon).apply { - shape = LauncherIconView.getDefaultShape(context) icon = iconRepository.getIconIfCached(file) + shape = LauncherIconView.currentShape job = rootView.scope.launch { rootView.lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { launch { @@ -63,6 +63,11 @@ class FileDetailRepresentation : Representation, KoinComponent { badge = it } } + launch { + LauncherIconView.getDefaultShape().collectLatest { + shape = it + } + } } } } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/FileListRepresentation.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/FileListRepresentation.kt index 933cec6e..3a6c1315 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/FileListRepresentation.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/FileListRepresentation.kt @@ -44,8 +44,8 @@ class FileListRepresentation : Representation, KoinComponent { findViewById(R.id.fileLabel).text = file.label findViewById(R.id.fileInfo).text = file.getFileType(context) findViewById(R.id.icon).apply { - shape = LauncherIconView.getDefaultShape(context) icon = iconRepository.getIconIfCached(file) + shape = LauncherIconView.currentShape job = rootView.scope.launch { rootView.lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { launch { @@ -59,6 +59,11 @@ class FileListRepresentation : Representation, KoinComponent { badge = it } } + launch { + LauncherIconView.getDefaultShape().collectLatest { + shape = it + } + } } } } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ShortcutDetailRepresentation.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ShortcutDetailRepresentation.kt index 07483317..23b7da10 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ShortcutDetailRepresentation.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/ShortcutDetailRepresentation.kt @@ -47,8 +47,8 @@ class AppShortcutDetailRepresentation : Representation, KoinComponent { setOnLongClickListener(null) findViewById(R.id.appName).text = appShortcut.label findViewById(R.id.icon).apply { - shape = LauncherIconView.getDefaultShape(context) icon = iconRepository.getIconIfCached(appShortcut) + shape = LauncherIconView.currentShape job = rootView.scope.launch { rootView.lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { launch { @@ -62,6 +62,11 @@ class AppShortcutDetailRepresentation : Representation, KoinComponent { badge = it } } + launch { + LauncherIconView.getDefaultShape().collectLatest { + shape = it + } + } } } } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WebsiteDetailRepresentation.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WebsiteDetailRepresentation.kt index b674536b..b42a3170 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WebsiteDetailRepresentation.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WebsiteDetailRepresentation.kt @@ -11,7 +11,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import androidx.transition.Scene import coil.load -import coil.size.Scale import de.mm20.launcher2.icons.IconRepository import de.mm20.launcher2.ktx.dp import de.mm20.launcher2.ktx.lifecycleOwner @@ -74,14 +73,21 @@ class WebsiteDetailRepresentation : Representation, KoinComponent { label.transitionName = null websiteFavIcon.transitionName = "icon" websiteFavIcon.apply { - shape = LauncherIconView.getDefaultShape(context) icon = iconRepository.getIconIfCached(website) + shape = LauncherIconView.currentShape job = rootView.scope.launch { rootView.lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - iconRepository.getIcon(website, (84 * rootView.dp).toInt()) - .collectLatest { - icon = it + launch { + iconRepository.getIcon(website, (84 * rootView.dp).toInt()) + .collectLatest { + icon = it + } + } + launch { + LauncherIconView.getDefaultShape().collectLatest { + shape = it } + } } } } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WebsiteListRepresentation.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WebsiteListRepresentation.kt index b66a5614..d37befc3 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WebsiteListRepresentation.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/WebsiteListRepresentation.kt @@ -77,8 +77,8 @@ class WebsiteListRepresentation : Representation, KoinComponent { label.transitionName = null websiteFavIcon.transitionName = "icon" websiteFavIcon.apply { - shape = LauncherIconView.getDefaultShape(context) icon = iconRepository.getIconIfCached(website) + shape = LauncherIconView.currentShape job = rootView.scope.launch { rootView.lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { launch { @@ -92,6 +92,11 @@ class WebsiteListRepresentation : Representation, KoinComponent { badge = it } } + launch { + LauncherIconView.getDefaultShape().collectLatest { + shape = it + } + } } } } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/view/LauncherIconView.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/view/LauncherIconView.kt index 02490d04..b1775e57 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/view/LauncherIconView.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/view/LauncherIconView.kt @@ -7,37 +7,25 @@ import android.graphics.* import android.graphics.drawable.AdaptiveIconDrawable import android.os.Build import android.util.AttributeSet -import android.util.Log import android.view.HapticFeedbackConstants import android.view.MotionEvent import android.view.View import android.view.ViewConfiguration import androidx.annotation.RequiresApi -import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LiveData -import androidx.lifecycle.Observer -import androidx.lifecycle.repeatOnLifecycle import com.bartoszlipinski.viewpropertyobjectanimator.ViewPropertyObjectAnimator import de.mm20.launcher2.badges.Badge -import de.mm20.launcher2.badges.BadgeRepository +import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.dp import de.mm20.launcher2.ktx.toRectF -import de.mm20.launcher2.icons.LauncherIcon -import de.mm20.launcher2.ktx.lifecycleOwner -import de.mm20.launcher2.ktx.lifecycleScope -import de.mm20.launcher2.preferences.IconShape -import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.Settings.IconSettings.IconShape import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.legacy.helper.BitmapHolder -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.* import org.koin.core.component.KoinComponent -import org.koin.core.component.inject +import org.koin.core.component.get import java.lang.Math.pow -import java.lang.Runnable import kotlin.math.abs import kotlin.math.hypot import kotlin.math.roundToInt @@ -45,17 +33,19 @@ import kotlin.math.roundToInt class LauncherIconView : View, KoinComponent { constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super(context, attrs, defStyleRes) + constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super( + context, + attrs, + defStyleRes + ) var shape: IconShape set(value) { - if (value == IconShape.PLATFORM_DEFAULT) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - platformShape = getSystemShape() - transformMatrix = Matrix() - platformShapeBounds = RectF() - field = value - } else field = IconShape.CIRCLE + if (value == IconShape.PlatformDefault) { + platformShape = getSystemShape() + transformMatrix = Matrix() + platformShapeBounds = RectF() + field = value } else { platformShape = null transformMatrix = null @@ -108,7 +98,7 @@ class LauncherIconView : View, KoinComponent { } init { - shape = IconShape.CIRCLE + shape = IconShape.Circle setLayerType(LAYER_TYPE_SOFTWARE, null) } @@ -200,34 +190,45 @@ class LauncherIconView : View, KoinComponent { if (bg != null) { bg.bounds = bmpDrawRect when (shape) { - IconShape.PLATFORM_DEFAULT -> { + IconShape.PlatformDefault -> { path.rewind() val matrix = transformMatrix!! val bounds = platformShapeBounds!! val shape = platformShape!! shape.computeBounds(bounds, true) - matrix.setRectToRect(bounds, badgeRect.also { drawRect.toRectF(it) }, Matrix.ScaleToFit.CENTER) + matrix.setRectToRect( + bounds, + badgeRect.also { drawRect.toRectF(it) }, + Matrix.ScaleToFit.CENTER + ) path.rewind() shape.transform(matrix, path) canvas.drawPath(path, maskPaint) } - IconShape.CIRCLE -> { - canvas.drawOval(drawRect.left.toFloat(), drawRect.top.toFloat(), drawRect.right.toFloat(), drawRect.bottom.toFloat(), maskPaint) - } - IconShape.SQUARE -> { - canvas.drawRect(drawRect, maskPaint) - } - IconShape.ROUNDED_SQUARE -> { - canvas.drawRoundRect(drawRect.left.toFloat(), - drawRect.top.toFloat(), - drawRect.right.toFloat(), - drawRect.bottom.toFloat(), - width * 0.125f, - height * 0.125f, - maskPaint + IconShape.Circle -> { + canvas.drawOval( + drawRect.left.toFloat(), + drawRect.top.toFloat(), + drawRect.right.toFloat(), + drawRect.bottom.toFloat(), + maskPaint ) } - IconShape.TRIANGLE -> { + IconShape.Square -> { + canvas.drawRect(drawRect, maskPaint) + } + IconShape.RoundedSquare -> { + canvas.drawRoundRect( + drawRect.left.toFloat(), + drawRect.top.toFloat(), + drawRect.right.toFloat(), + drawRect.bottom.toFloat(), + width * 0.125f, + height * 0.125f, + maskPaint + ) + } + IconShape.Triangle -> { path.rewind() var cx = drawRect.left.toFloat() var cy = drawRect.top + drawRect.height().toFloat() * 0.86f @@ -248,63 +249,157 @@ class LauncherIconView : View, KoinComponent { canvas.drawArc(cx - r, cy - r, cx + r, cy + r, 60f, 60f, true, maskPaint) } - IconShape.SQUIRCLE -> { + IconShape.Squircle -> { path.rewind() val radius = drawRect.width() / 2 val radiusToPow = pow(radius.toDouble(), 3.0) path.moveTo(-radius.toFloat(), 0f) for (x in -radius..radius) - path.lineTo(x.toFloat(), Math.cbrt(radiusToPow - Math.abs(x * x * x)).toFloat()) + path.lineTo( + x.toFloat(), + Math.cbrt(radiusToPow - Math.abs(x * x * x)).toFloat() + ) for (x in radius downTo -radius) - path.lineTo(x.toFloat(), (-Math.cbrt(radiusToPow - Math.abs(x * x * x))).toFloat()) + path.lineTo( + x.toFloat(), + (-Math.cbrt(radiusToPow - Math.abs(x * x * x))).toFloat() + ) path.close() canvas.save() canvas.translate(width / 2f, height / 2f) canvas.drawPath(path, maskPaint) canvas.restore() } - IconShape.HEXAGON -> { + IconShape.Hexagon -> { path.rewind() - path.moveTo(drawRect.left + drawRect.width() * 0.25f, drawRect.top + drawRect.height() * 0.933f) - path.lineTo(drawRect.left + drawRect.width() * 0.75f, drawRect.top + drawRect.height() * 0.933f) - path.lineTo(drawRect.left + drawRect.width() * 1.0f, drawRect.top + drawRect.height() * 0.5f) - path.lineTo(drawRect.left + drawRect.width() * 0.75f, drawRect.top + drawRect.height() * 0.067f) - path.lineTo(drawRect.left + drawRect.width() * 0.25f, drawRect.top + drawRect.height() * 0.067f) + path.moveTo( + drawRect.left + drawRect.width() * 0.25f, + drawRect.top + drawRect.height() * 0.933f + ) + path.lineTo( + drawRect.left + drawRect.width() * 0.75f, + drawRect.top + drawRect.height() * 0.933f + ) + path.lineTo( + drawRect.left + drawRect.width() * 1.0f, + drawRect.top + drawRect.height() * 0.5f + ) + path.lineTo( + drawRect.left + drawRect.width() * 0.75f, + drawRect.top + drawRect.height() * 0.067f + ) + path.lineTo( + drawRect.left + drawRect.width() * 0.25f, + drawRect.top + drawRect.height() * 0.067f + ) path.lineTo(drawRect.left.toFloat(), drawRect.top + drawRect.height() * 0.5f) path.close() canvas.drawPath(path, maskPaint) } - IconShape.HEART -> { + IconShape.EasterEgg -> { path.rewind() - path.moveTo(0.49999999f * drawRect.width() + drawRect.left, 1f * drawRect.height() + drawRect.top) - path.lineTo(0.42749999f * drawRect.width() + drawRect.left, 0.9339999999999999f * drawRect.height() + drawRect.top) - path.cubicTo(0.16999998f * drawRect.width() + drawRect.left, 0.7005004f * drawRect.height() + drawRect.top, 0f + drawRect.left, 0.5460004f * drawRect.height() + drawRect.top, 0f + drawRect.left, 0.3575003f * drawRect.height() + drawRect.top) - path.cubicTo(0f + drawRect.left, 0.2030004f * drawRect.height() + drawRect.top, 0.12100002f * drawRect.width() + drawRect.left, 0.0825004f * drawRect.height() + drawRect.top, 0.275f * drawRect.width() + drawRect.left, 0.0825004f * drawRect.height() + drawRect.top) - path.cubicTo(0.362f * drawRect.width() + drawRect.left, 0.0825004f * drawRect.height() + drawRect.top, 0.4455f * drawRect.width() + drawRect.left, 0.123f * drawRect.height() + drawRect.top, 0.5f * drawRect.width() + drawRect.left, 0.1865003f * drawRect.height() + drawRect.top) - path.cubicTo(0.55449999f * drawRect.width() + drawRect.left, 0.123f * drawRect.height() + drawRect.top, 0.638f * drawRect.width() + drawRect.left, 0.0825f * drawRect.height() + drawRect.top, 0.725f * drawRect.width() + drawRect.left, 0.0825f * drawRect.height() + drawRect.top) - path.cubicTo(0.87900006f * drawRect.width() + drawRect.left, 0.0825004f * drawRect.height() + drawRect.top, 1f * drawRect.width() + drawRect.left, 0.2030004f * drawRect.height() + drawRect.top, 1f * drawRect.width() + drawRect.left, 0.3575003f * drawRect.height() + drawRect.top) - path.cubicTo(1f * drawRect.width() + drawRect.left, 0.5460004f * drawRect.height() + drawRect.top, 0.82999999f * drawRect.width() + drawRect.left, 0.7005004f * drawRect.height() + drawRect.top, 0.57250001f * drawRect.width() + drawRect.left, 0.9340004f * drawRect.height() + drawRect.top) + path.moveTo( + 0.49999999f * drawRect.width() + drawRect.left, + 1f * drawRect.height() + drawRect.top + ) + path.lineTo( + 0.42749999f * drawRect.width() + drawRect.left, + 0.9339999999999999f * drawRect.height() + drawRect.top + ) + path.cubicTo( + 0.16999998f * drawRect.width() + drawRect.left, + 0.7005004f * drawRect.height() + drawRect.top, + 0f + drawRect.left, + 0.5460004f * drawRect.height() + drawRect.top, + 0f + drawRect.left, + 0.3575003f * drawRect.height() + drawRect.top + ) + path.cubicTo( + 0f + drawRect.left, + 0.2030004f * drawRect.height() + drawRect.top, + 0.12100002f * drawRect.width() + drawRect.left, + 0.0825004f * drawRect.height() + drawRect.top, + 0.275f * drawRect.width() + drawRect.left, + 0.0825004f * drawRect.height() + drawRect.top + ) + path.cubicTo( + 0.362f * drawRect.width() + drawRect.left, + 0.0825004f * drawRect.height() + drawRect.top, + 0.4455f * drawRect.width() + drawRect.left, + 0.123f * drawRect.height() + drawRect.top, + 0.5f * drawRect.width() + drawRect.left, + 0.1865003f * drawRect.height() + drawRect.top + ) + path.cubicTo( + 0.55449999f * drawRect.width() + drawRect.left, + 0.123f * drawRect.height() + drawRect.top, + 0.638f * drawRect.width() + drawRect.left, + 0.0825f * drawRect.height() + drawRect.top, + 0.725f * drawRect.width() + drawRect.left, + 0.0825f * drawRect.height() + drawRect.top + ) + path.cubicTo( + 0.87900006f * drawRect.width() + drawRect.left, + 0.0825004f * drawRect.height() + drawRect.top, + 1f * drawRect.width() + drawRect.left, + 0.2030004f * drawRect.height() + drawRect.top, + 1f * drawRect.width() + drawRect.left, + 0.3575003f * drawRect.height() + drawRect.top + ) + path.cubicTo( + 1f * drawRect.width() + drawRect.left, + 0.5460004f * drawRect.height() + drawRect.top, + 0.82999999f * drawRect.width() + drawRect.left, + 0.7005004f * drawRect.height() + drawRect.top, + 0.57250001f * drawRect.width() + drawRect.left, + 0.9340004f * drawRect.height() + drawRect.top + ) path.close() canvas.drawPath(path, maskPaint) } - IconShape.PENTAGON -> { + IconShape.Pentagon -> { path.rewind() - path.moveTo(0.49997027f * drawRect.width() + drawRect.left, 0.0060308f * drawRect.height() + drawRect.top) - path.lineTo(0.99994053f * drawRect.width() + drawRect.left, 0.36928048f * drawRect.height() + drawRect.top) - path.lineTo(0.80896887f * drawRect.width() + drawRect.left, 0.95703078f * drawRect.height() + drawRect.top) - path.lineTo(0.19097162f * drawRect.width() + drawRect.left, 0.95703076f * drawRect.height() + drawRect.top) - path.lineTo(drawRect.left.toFloat(), 0.36928045f * drawRect.height() + drawRect.top) + path.moveTo( + 0.49997027f * drawRect.width() + drawRect.left, + 0.0060308f * drawRect.height() + drawRect.top + ) + path.lineTo( + 0.99994053f * drawRect.width() + drawRect.left, + 0.36928048f * drawRect.height() + drawRect.top + ) + path.lineTo( + 0.80896887f * drawRect.width() + drawRect.left, + 0.95703078f * drawRect.height() + drawRect.top + ) + path.lineTo( + 0.19097162f * drawRect.width() + drawRect.left, + 0.95703076f * drawRect.height() + drawRect.top + ) + path.lineTo( + drawRect.left.toFloat(), + 0.36928045f * drawRect.height() + drawRect.top + ) path.close() canvas.drawPath(path, maskPaint) } } c.save() - c.scale(backgroundScale, backgroundScale, bmpDrawRect.centerX().toFloat(), bmpDrawRect.centerY().toFloat()) + c.scale( + backgroundScale, + backgroundScale, + bmpDrawRect.centerX().toFloat(), + bmpDrawRect.centerY().toFloat() + ) bg.draw(c) c.restore() } c.save() - c.scale(foregroundScale, foregroundScale, bmpDrawRect.centerX().toFloat(), bmpDrawRect.centerY().toFloat()) + c.scale( + foregroundScale, + foregroundScale, + bmpDrawRect.centerX().toFloat(), + bmpDrawRect.centerY().toFloat() + ) fg.draw(c) c.restore() if (bg != null) { @@ -314,26 +409,33 @@ class LauncherIconView : View, KoinComponent { } if (bg != null) { when (shape) { - IconShape.CIRCLE -> { - canvas.drawOval(drawRect.left.toFloat(), drawRect.top.toFloat(), drawRect.right.toFloat(), drawRect.bottom.toFloat(), shadowPaint) - } - IconShape.SQUARE -> { - canvas.drawRect(drawRect, shadowPaint) - } - IconShape.ROUNDED_SQUARE -> { - canvas.drawRoundRect(drawRect.left.toFloat(), - drawRect.top.toFloat(), - drawRect.right.toFloat(), - drawRect.bottom.toFloat(), - width * 0.125f, - height * 0.125f, - shadowPaint + IconShape.Circle -> { + canvas.drawOval( + drawRect.left.toFloat(), + drawRect.top.toFloat(), + drawRect.right.toFloat(), + drawRect.bottom.toFloat(), + shadowPaint ) } - IconShape.TRIANGLE, IconShape.HEXAGON, IconShape.HEART, IconShape.PENTAGON, IconShape.PLATFORM_DEFAULT -> { + IconShape.Square -> { + canvas.drawRect(drawRect, shadowPaint) + } + IconShape.RoundedSquare -> { + canvas.drawRoundRect( + drawRect.left.toFloat(), + drawRect.top.toFloat(), + drawRect.right.toFloat(), + drawRect.bottom.toFloat(), + width * 0.125f, + height * 0.125f, + shadowPaint + ) + } + IconShape.Triangle, IconShape.Hexagon, IconShape.EasterEgg, IconShape.Pentagon, IconShape.PlatformDefault -> { canvas.drawPath(path, shadowPaint) } - IconShape.SQUIRCLE -> { + IconShape.Squircle -> { canvas.save() canvas.translate(width / 2f, height / 2f) canvas.drawPath(path, shadowPaint) @@ -361,12 +463,18 @@ class LauncherIconView : View, KoinComponent { canvas.drawArc(badgeRect, 270f, it * 360, true, badgeProgressPaint) } badgeIcon?.let { - it.setBounds((drawRect.right - badgeSize * 0.9f).toInt(), - (drawRect.bottom - badgeSize * 0.9f).toInt(), - (drawRect.right - badgeSize * 0.1f).toInt(), - (drawRect.bottom - badgeSize * 0.1f).toInt() + it.setBounds( + (drawRect.right - badgeSize * 0.9f).toInt(), + (drawRect.bottom - badgeSize * 0.9f).toInt(), + (drawRect.right - badgeSize * 0.1f).toInt(), + (drawRect.bottom - badgeSize * 0.1f).toInt() + ) + it.setBounds( + badgeRect.left.roundToInt(), + badgeRect.top.roundToInt(), + badgeRect.right.roundToInt(), + badgeRect.bottom.roundToInt() ) - it.setBounds(badgeRect.left.roundToInt(), badgeRect.top.roundToInt(), badgeRect.right.roundToInt(), badgeRect.bottom.roundToInt()) it.draw(canvas) return } @@ -375,7 +483,12 @@ class LauncherIconView : View, KoinComponent { val textSize = (1f - 0.1f - text.length * 0.1f) * badgeSize badgeTextPaint.textSize = textSize badgeTextPaint.getTextBounds(text, 0, text.length, textBounds) - canvas.drawText(it.toString(), badgeRect.centerX(), badgeRect.centerY() - textBounds.exactCenterY(), badgeTextPaint) + canvas.drawText( + it.toString(), + badgeRect.centerX(), + badgeRect.centerY() - textBounds.exactCenterY(), + badgeTextPaint + ) } } @@ -398,7 +511,10 @@ class LauncherIconView : View, KoinComponent { downX = ev.rawX downY = ev.rawY longClicked = false - handler?.postDelayed(longClickRunnable, ViewConfiguration.getLongPressTimeout().toLong()) + handler?.postDelayed( + longClickRunnable, + ViewConfiguration.getLongPressTimeout().toLong() + ) return true } MotionEvent.ACTION_MOVE -> { @@ -428,9 +544,9 @@ class LauncherIconView : View, KoinComponent { private fun animateTouchUp() { AnimatorSet().also { it.playTogether( - ViewPropertyObjectAnimator.animate(this).translationZ(0f).get(), - ObjectAnimator.ofFloat(this, "foregroundScale", icon?.foregroundScale ?: 1f), - ObjectAnimator.ofFloat(this, "backgroundScale", icon?.backgroundScale ?: 1f) + ViewPropertyObjectAnimator.animate(this).translationZ(0f).get(), + ObjectAnimator.ofFloat(this, "foregroundScale", icon?.foregroundScale ?: 1f), + ObjectAnimator.ofFloat(this, "backgroundScale", icon?.backgroundScale ?: 1f) ) it.duration = 300 it.start() @@ -440,11 +556,15 @@ class LauncherIconView : View, KoinComponent { private fun animateTouchDown() { AnimatorSet().also { it.playTogether( - ViewPropertyObjectAnimator.animate(this).translationZ(2 * dp).get(), - ObjectAnimator.ofFloat(this, "foregroundScale", (icon?.foregroundScale - ?: 1f) * 0.8f), - ObjectAnimator.ofFloat(this, "backgroundScale", (icon?.backgroundScale - ?: 1f) * 1.2f) + ViewPropertyObjectAnimator.animate(this).translationZ(2 * dp).get(), + ObjectAnimator.ofFloat( + this, "foregroundScale", (icon?.foregroundScale + ?: 1f) * 0.8f + ), + ObjectAnimator.ofFloat( + this, "backgroundScale", (icon?.backgroundScale + ?: 1f) * 1.2f + ) ) it.duration = 250 it.start() @@ -452,12 +572,16 @@ class LauncherIconView : View, KoinComponent { } - companion object { + companion object: KoinComponent { - fun getDefaultShape(context: Context): IconShape { - if (LauncherPreferences.instance.easterEggEnabled) return IconShape.HEART - return LauncherPreferences.instance.iconShape.let { - return@let if (it != IconShape.HEART) it else IconShape.PLATFORM_DEFAULT + var currentShape: IconShape = IconShape.PlatformDefault + + fun getDefaultShape(): Flow = channelFlow { + send(currentShape) + val dataStore: LauncherDataStore = get() + dataStore.data.map { it.icons.shape }.distinctUntilChanged().collectLatest { + currentShape = it + send(it) } } } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt index 5820fc8d..2d3f0f95 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt @@ -1,31 +1,45 @@ package de.mm20.launcher2.ui.settings.appearance +import android.content.Context +import android.graphics.drawable.ColorDrawable +import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.content.res.AppCompatResources +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton +import androidx.compose.foundation.lazy.GridCells +import androidx.compose.foundation.lazy.LazyVerticalGrid +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.compose.ui.window.Dialog import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.HorizontalPagerIndicator import com.google.accompanist.pager.rememberPagerState +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.ktx.dp +import de.mm20.launcher2.preferences.IconShape import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme +import de.mm20.launcher2.preferences.Settings.IconSettings import de.mm20.launcher2.preferences.Settings.SearchBarSettings import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.preferences.* import de.mm20.launcher2.ui.launcher.search.SearchBar import de.mm20.launcher2.ui.launcher.search.SearchBarLevel +import de.mm20.launcher2.ui.legacy.view.LauncherIconView import kotlinx.coroutines.delay import kotlinx.coroutines.isActive @@ -96,6 +110,17 @@ fun AppearanceSettingsScreen() { } ) } + PreferenceCategory(stringResource(R.string.preference_category_icons)) { + val iconShape by viewModel.iconShape.observeAsState() + IconShapePreference( + title = stringResource(R.string.preference_icon_shape), + summary = getShapeName(iconShape), + value = iconShape, + onValueChanged = { + viewModel.setIconShape(it) + } + ) + } PreferenceCategory(stringResource(R.string.preference_category_searchbar)) { val searchBarStyle by viewModel.searchBarStyle.observeAsState() SearchBarStylePreference( @@ -123,14 +148,15 @@ fun SearchBarStylePreference( Preference(title = title, summary = summary, onClick = { showDialog = true }) if (showDialog && value != null) { val styles = remember { - SearchBarSettings.SearchBarStyle.values().filter { it != SearchBarSettings.SearchBarStyle.UNRECOGNIZED } + SearchBarSettings.SearchBarStyle.values() + .filter { it != SearchBarSettings.SearchBarStyle.UNRECOGNIZED } } val pagerState = rememberPagerState(styles.indexOf(value)) var level by remember { mutableStateOf(SearchBarLevel.Resting) } var previewSearchValue by remember { mutableStateOf("") } LaunchedEffect(null) { - while(isActive) { + while (isActive) { delay(2000) level = SearchBarLevel.Active delay(1000) @@ -181,11 +207,108 @@ fun SearchBarStylePreference( .padding(bottom = 16.dp) .background(MaterialTheme.colorScheme.secondary) ) { - SearchBar(level = level, style = styles[it], websearches = emptyList(), value = previewSearchValue, onValueChange = {}) + SearchBar( + level = level, + style = styles[it], + websearches = emptyList(), + value = previewSearchValue, + onValueChange = {}) } HorizontalPagerIndicator(pagerState = pagerState) } } ) } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun IconShapePreference( + title: String, + summary: String? = null, + value: IconSettings.IconShape?, + onValueChanged: (IconSettings.IconShape) -> Unit +) { + var showDialog by remember { mutableStateOf(false) } + Preference(title = title, summary = summary, onClick = { showDialog = true }) + + if (showDialog && value != null) { + val shapes = remember { + IconSettings.IconShape.values() + .filter { it != IconSettings.IconShape.UNRECOGNIZED && it != IconSettings.IconShape.EasterEgg } + } + Dialog(onDismissRequest = { showDialog = false }) { + Surface( + tonalElevation = 16.dp, + shadowElevation = 16.dp, + shape = RoundedCornerShape(16.dp), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp), + ) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = title, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding( + start = 24.dp, end = 24.dp, top = 16.dp, bottom = 8.dp + ) + ) + LazyVerticalGrid( + cells = GridCells.Adaptive(96.dp), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp, start = 16.dp, end = 16.dp) + ) { + items(shapes) { + Column(modifier = Modifier + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally) { + AndroidView(factory = { context -> + LauncherIconView(context).apply { + shape = it + icon = LauncherIcon( + foreground = AppCompatResources.getDrawable(context, R.mipmap.ic_launcher_foreground)!!, + background = ColorDrawable(context.getColor(R.color.ic_launcher_background)) + ) + setOnClickListener { _ -> + onValueChanged(it) + showDialog = false + } + layoutParams = ViewGroup.LayoutParams( + (48 * context.dp).toInt(), + (48 * context.dp).toInt(), + ) + } + }) + Text( + getShapeName(it) ?: "", + textAlign = TextAlign.Center, + style = MaterialTheme.typography.labelMedium, + modifier = Modifier.padding(top = 4.dp)) + } + } + } + } + } + } + } +} + + +@Composable +private fun getShapeName(shape: IconSettings.IconShape?): String? { + return stringResource(when (shape) { + IconSettings.IconShape.Triangle -> R.string.preference_icon_shape_triangle + IconSettings.IconShape.Hexagon -> R.string.preference_icon_shape_hexagon + IconSettings.IconShape.RoundedSquare -> R.string.preference_icon_shape_rounded_square + IconSettings.IconShape.Squircle -> R.string.preference_icon_shape_squircle + IconSettings.IconShape.Square -> R.string.preference_icon_shape_square + IconSettings.IconShape.Pentagon -> R.string.preference_icon_shape_pentagon + IconSettings.IconShape.PlatformDefault -> R.string.preference_icon_shape_platform + IconSettings.IconShape.Circle -> R.string.preference_icon_shape_circle + else -> return null + }) } \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt index 03f3dfdb..ff4d4590 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme import de.mm20.launcher2.preferences.Settings.SearchBarSettings @@ -70,8 +71,23 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent { viewModelScope.launch { dataStore.updateData { it.toBuilder() - .setSearchBar(it.searchBar.toBuilder() - .setSearchBarStyle(searchBarStyle) + .setSearchBar( + it.searchBar.toBuilder() + .setSearchBarStyle(searchBarStyle) + ) + .build() + } + } + } + + val iconShape = dataStore.data.map { it.icons.shape }.asLiveData() + fun setIconShape(iconShape: Settings.IconSettings.IconShape) { + viewModelScope.launch { + dataStore.updateData { + it.toBuilder() + .setIcons( + it.icons.toBuilder() + .setShape(iconShape) ) .build() }