Migrate icon shape preference
@ -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<Preference>("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<LauncherIconView>(R.id.icon).also { iconView ->
|
||||
iconView.icon = launcherIcon
|
||||
iconView.shape = shape.first
|
||||
}
|
||||
view.findViewById<TextView>(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()
|
||||
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -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()
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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<TextView>(R.id.appName).text = application.label
|
||||
val iconView = findViewById<LauncherIconView>(R.id.icon).apply {
|
||||
shape = LauncherIconView.getDefaultShape(context)
|
||||
icon = iconRepository.getIconIfCached(application)
|
||||
shape = LauncherIconView.currentShape
|
||||
}
|
||||
|
||||
val notificationView = findViewById<ChipGroup>(R.id.notifications)
|
||||
@ -107,6 +106,11 @@ class ApplicationDetailRepresentation : Representation, KoinComponent {
|
||||
updateNotifications(notificationView, it)
|
||||
}
|
||||
}
|
||||
launch {
|
||||
LauncherIconView.getDefaultShape().collectLatest {
|
||||
iconView.shape = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
findViewById<SwipeCardView>(R.id.appCard).also {
|
||||
|
||||
@ -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<LauncherIconView>(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 {
|
||||
|
||||
@ -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<LauncherIconView>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<LauncherIconView>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,8 +48,8 @@ class FileDetailRepresentation : Representation, KoinComponent {
|
||||
findViewById<TextView>(R.id.fileLabel).text = file.label
|
||||
findViewById<TextView>(R.id.fileInfo).text = getInfo(context, file)
|
||||
findViewById<LauncherIconView>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,8 +44,8 @@ class FileListRepresentation : Representation, KoinComponent {
|
||||
findViewById<TextView>(R.id.fileLabel).text = file.label
|
||||
findViewById<TextView>(R.id.fileInfo).text = file.getFileType(context)
|
||||
findViewById<LauncherIconView>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,8 +47,8 @@ class AppShortcutDetailRepresentation : Representation, KoinComponent {
|
||||
setOnLongClickListener(null)
|
||||
findViewById<TextView>(R.id.appName).text = appShortcut.label
|
||||
findViewById<LauncherIconView>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<IconShape> = channelFlow {
|
||||
send(currentShape)
|
||||
val dataStore: LauncherDataStore = get()
|
||||
dataStore.data.map { it.icons.shape }.distinctUntilChanged().collectLatest {
|
||||
currentShape = it
|
||||
send(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
})
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
|
||||