diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 841c9bd..5a1d5a0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,9 +21,15 @@
android:theme="@style/launcherBaseTheme"
tools:ignore="UnusedAttribute">
+
+
diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt b/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt
index 9a2dc62..a883922 100644
--- a/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/actions/Action.kt
@@ -6,14 +6,18 @@ import android.content.SharedPreferences.Editor
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.widget.Toast
+import androidx.core.content.edit
import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.preferences.LauncherPreferences
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
-import androidx.core.content.edit
+/**
+ * Represents an action that can be bound to a [Gesture].
+ * There are four types of actions: [AppAction], [ShortcutAction], [LauncherAction] and [WidgetPanelAction]
+ */
@Serializable
sealed interface Action {
fun invoke(context: Context, rect: Rect? = null): Boolean
@@ -21,6 +25,10 @@ sealed interface Action {
fun getIcon(context: Context): Drawable?
fun isAvailable(context: Context): Boolean
+ fun showConfigurationDialog(context: Context, onSuccess: (Action) -> Unit) {
+ onSuccess(this)
+ }
+
// Can the action be used to reach µLauncher settings?
fun canReachSettings(): Boolean
diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/WidgetPanelAction.kt b/app/src/main/java/de/jrpie/android/launcher/actions/WidgetPanelAction.kt
new file mode 100644
index 0000000..d7829a6
--- /dev/null
+++ b/app/src/main/java/de/jrpie/android/launcher/actions/WidgetPanelAction.kt
@@ -0,0 +1,83 @@
+package de.jrpie.android.launcher.actions
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.view.View
+import android.widget.TextView
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.core.content.res.ResourcesCompat
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import de.jrpie.android.launcher.R
+import de.jrpie.android.launcher.ui.widgets.WidgetPanelActivity
+import de.jrpie.android.launcher.ui.widgets.manage.EXTRA_PANEL_ID
+import de.jrpie.android.launcher.ui.widgets.manage.WidgetPanelsRecyclerAdapter
+import de.jrpie.android.launcher.widgets.WidgetPanel
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+@SerialName("action:panel")
+class WidgetPanelAction(val widgetPanelId: Int) : Action {
+
+ override fun invoke(context: Context, rect: Rect?): Boolean {
+
+ if (WidgetPanel.byId(widgetPanelId) == null) {
+ Toast.makeText(context, R.string.alert_widget_panel_not_found, Toast.LENGTH_LONG).show()
+ } else {
+ context.startActivity(Intent(context, WidgetPanelActivity::class.java).also {
+ it.putExtra(EXTRA_PANEL_ID, widgetPanelId)
+ })
+ }
+ return true
+ }
+
+ override fun label(context: Context): String {
+ return WidgetPanel.byId(widgetPanelId)?.label
+ ?: context.getString(R.string.list_other_open_widget_panel)
+ }
+
+ override fun isAvailable(context: Context): Boolean {
+ return true
+ }
+
+ override fun canReachSettings(): Boolean {
+ return false
+ }
+
+ override fun getIcon(context: Context): Drawable? {
+ return ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.baseline_widgets_24,
+ context.theme
+ )
+ }
+
+ override fun showConfigurationDialog(context: Context, onSuccess: (Action) -> Unit) {
+ AlertDialog.Builder(context, R.style.AlertDialogCustom).apply {
+ setTitle(R.string.dialog_select_widget_panel_title)
+ setNegativeButton(R.string.dialog_cancel) { _, _ -> }
+ setView(R.layout.dialog_select_widget_panel)
+ }.create().also { it.show() }.also { alertDialog ->
+ val infoTextView =
+ alertDialog.findViewById(R.id.dialog_select_widget_panel_info)
+ alertDialog.findViewById(R.id.dialog_select_widget_panel_recycler)
+ ?.apply {
+ setHasFixedSize(true)
+ layoutManager = LinearLayoutManager(alertDialog.context)
+ adapter =
+ WidgetPanelsRecyclerAdapter(alertDialog.context, false) { widgetPanel ->
+ onSuccess(WidgetPanelAction(widgetPanel.id))
+ alertDialog.dismiss()
+ }
+ if (adapter?.itemCount == 0) {
+ infoTextView?.visibility = View.VISIBLE
+ }
+ }
+ }
+ true
+ }
+}
diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java b/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java
index 575346a..d509ef2 100644
--- a/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java
+++ b/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java
@@ -8,6 +8,7 @@ import de.jrpie.android.launcher.actions.lock.LockMethod;
import de.jrpie.android.launcher.preferences.serialization.MapAbstractAppInfoStringPreferenceSerializer;
import de.jrpie.android.launcher.preferences.serialization.SetAbstractAppInfoPreferenceSerializer;
import de.jrpie.android.launcher.preferences.serialization.SetPinnedShortcutInfoPreferenceSerializer;
+import de.jrpie.android.launcher.preferences.serialization.SetWidgetPanelSerializer;
import de.jrpie.android.launcher.preferences.serialization.SetWidgetSerializer;
import de.jrpie.android.launcher.preferences.theme.Background;
import de.jrpie.android.launcher.preferences.theme.ColorTheme;
@@ -84,7 +85,8 @@ import eu.jonahbauer.android.preference.annotations.Preferences;
@Preference(name = "lock_method", type = LockMethod.class, defaultValue = "DEVICE_ADMIN"),
}),
@PreferenceGroup(name = "widgets", prefix = "settings_widgets_", suffix= "_key", value = {
- @Preference(name = "widgets", type = Set.class, serializer = SetWidgetSerializer.class)
+ @Preference(name = "widgets", type = Set.class, serializer = SetWidgetSerializer.class),
+ @Preference(name = "custom_panels", type = Set.class, serializer = SetWidgetPanelSerializer.class)
}),
})
public final class LauncherPreferences$Config {}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/Preferences.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/Preferences.kt
index 332f4df..e8e717e 100644
--- a/app/src/main/java/de/jrpie/android/launcher/preferences/Preferences.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/preferences/Preferences.kt
@@ -2,18 +2,21 @@ package de.jrpie.android.launcher.preferences
import android.content.Context
import android.util.Log
+import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.BuildConfig
import de.jrpie.android.launcher.actions.Action
-import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.AbstractAppInfo
import de.jrpie.android.launcher.apps.AbstractAppInfo.Companion.INVALID_USER
+import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.DetailedAppInfo
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion1
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion2
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion3
+import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion4
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown
import de.jrpie.android.launcher.ui.HomeActivity
import de.jrpie.android.launcher.widgets.ClockWidget
+import de.jrpie.android.launcher.widgets.WidgetPanel
import de.jrpie.android.launcher.widgets.WidgetPosition
import de.jrpie.android.launcher.widgets.deleteAllWidgets
@@ -21,7 +24,7 @@ import de.jrpie.android.launcher.widgets.deleteAllWidgets
* Increase when breaking changes are introduced and write an appropriate case in
* `migratePreferencesToNewVersion`
*/
-const val PREFERENCE_VERSION = 4
+const val PREFERENCE_VERSION = 5
const val UNKNOWN_PREFERENCE_VERSION = -1
private const val TAG = "Launcher - Preferences"
@@ -43,18 +46,23 @@ fun migratePreferencesToNewVersion(context: Context) {
}
1 -> {
- migratePreferencesFromVersion1()
+ migratePreferencesFromVersion1(context)
Log.i(TAG, "migration of preferences complete (1 -> ${PREFERENCE_VERSION}).")
}
2 -> {
- migratePreferencesFromVersion2()
+ migratePreferencesFromVersion2(context)
Log.i(TAG, "migration of preferences complete (2 -> ${PREFERENCE_VERSION}).")
}
3 -> {
- migratePreferencesFromVersion3()
+ migratePreferencesFromVersion3(context)
Log.i(TAG, "migration of preferences complete (3 -> ${PREFERENCE_VERSION}).")
}
+ 4 -> {
+ migratePreferencesFromVersion4(context)
+ Log.i(TAG, "migration of preferences complete (4 -> ${PREFERENCE_VERSION}).")
+ }
+
else -> {
Log.w(
TAG,
@@ -78,7 +86,11 @@ fun resetPreferences(context: Context) {
LauncherPreferences.widgets().widgets(
setOf(
- ClockWidget(-500, WidgetPosition(1,4,10,3))
+ ClockWidget(
+ (context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
+ WidgetPosition(1, 3, 10, 4),
+ WidgetPanel.HOME.id
+ )
)
)
diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version1.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version1.kt
index a1cb022..6cd9819 100644
--- a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version1.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version1.kt
@@ -1,11 +1,13 @@
package de.jrpie.android.launcher.preferences.legacy
+import android.content.Context
+import androidx.core.content.edit
import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.AppAction
import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.actions.LauncherAction
-import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.AbstractAppInfo.Companion.INVALID_USER
+import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
import kotlinx.serialization.Serializable
@@ -13,7 +15,6 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.json.JSONException
import org.json.JSONObject
-import androidx.core.content.edit
@Serializable
@@ -129,7 +130,7 @@ private fun migrateAction(key: String) {
* Migrate preferences from version 1 (used until version j-0.0.18) to the current format
* (see [PREFERENCE_VERSION])
*/
-fun migratePreferencesFromVersion1() {
+fun migratePreferencesFromVersion1(context: Context) {
assert(LauncherPreferences.internal().versionCode() == 1)
Gesture.entries.forEach { g -> migrateAction(g.id) }
migrateAppInfoSet(LauncherPreferences.apps().keys().hidden())
@@ -137,5 +138,5 @@ fun migratePreferencesFromVersion1() {
migrateAppInfoStringMap(LauncherPreferences.apps().keys().customNames())
LauncherPreferences.internal().versionCode(2)
- migratePreferencesFromVersion2()
+ migratePreferencesFromVersion2(context)
}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version2.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version2.kt
index 4e6eae1..9714359 100644
--- a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version2.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version2.kt
@@ -1,5 +1,6 @@
package de.jrpie.android.launcher.preferences.legacy
+import android.content.Context
import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.actions.LauncherAction
@@ -11,10 +12,10 @@ import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
* Migrate preferences from version 2 (used until version 0.0.21) to the current format
* (see [PREFERENCE_VERSION])
*/
-fun migratePreferencesFromVersion2() {
+fun migratePreferencesFromVersion2(context: Context) {
assert(LauncherPreferences.internal().versionCode() == 2)
// previously there was no setting for this
Action.setActionForGesture(Gesture.BACK, LauncherAction.CHOOSE)
LauncherPreferences.internal().versionCode(3)
- migratePreferencesFromVersion3()
+ migratePreferencesFromVersion3(context)
}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version3.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version3.kt
index 4a9241f..e0a8447 100644
--- a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version3.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version3.kt
@@ -1,17 +1,17 @@
package de.jrpie.android.launcher.preferences.legacy
+import android.content.Context
import android.content.SharedPreferences
import android.content.SharedPreferences.Editor
-import de.jrpie.android.launcher.apps.AppInfo
+import androidx.core.content.edit
import de.jrpie.android.launcher.apps.AbstractAppInfo
+import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
import de.jrpie.android.launcher.preferences.serialization.MapAbstractAppInfoStringPreferenceSerializer
import de.jrpie.android.launcher.preferences.serialization.SetAbstractAppInfoPreferenceSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
-import java.util.HashSet
-import androidx.core.content.edit
/**
* Migrate preferences from version 3 (used until version 0.0.23) to the current format
@@ -70,8 +70,7 @@ private fun migrateMapAppInfoString(key: String, preferences: SharedPreferences,
}
}
-fun migratePreferencesFromVersion3() {
- assert(PREFERENCE_VERSION == 4)
+fun migratePreferencesFromVersion3(context: Context) {
assert(LauncherPreferences.internal().versionCode() == 3)
val preferences = LauncherPreferences.getSharedPreferences()
@@ -82,4 +81,5 @@ fun migratePreferencesFromVersion3() {
}
LauncherPreferences.internal().versionCode(4)
+ migratePreferencesFromVersion4(context)
}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version4.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version4.kt
new file mode 100644
index 0000000..d4c7441
--- /dev/null
+++ b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/Version4.kt
@@ -0,0 +1,27 @@
+package de.jrpie.android.launcher.preferences.legacy
+
+import android.content.Context
+import de.jrpie.android.launcher.Application
+import de.jrpie.android.launcher.preferences.LauncherPreferences
+import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
+import de.jrpie.android.launcher.widgets.ClockWidget
+import de.jrpie.android.launcher.widgets.WidgetPanel
+import de.jrpie.android.launcher.widgets.WidgetPosition
+
+fun migratePreferencesFromVersion4(context: Context) {
+ assert(PREFERENCE_VERSION == 5)
+ assert(LauncherPreferences.internal().versionCode() == 4)
+
+ LauncherPreferences.widgets().widgets(
+ setOf(
+ ClockWidget(
+ (context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
+ WidgetPosition(1, 3, 10, 4),
+ WidgetPanel.HOME.id
+ )
+ )
+ )
+
+
+ LauncherPreferences.internal().versionCode(5)
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/VersionUnknown.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/VersionUnknown.kt
index 2d1152d..f954b31 100644
--- a/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/VersionUnknown.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/preferences/legacy/VersionUnknown.kt
@@ -3,10 +3,10 @@ package de.jrpie.android.launcher.preferences.legacy
import android.content.Context
import android.content.SharedPreferences
import android.util.Log
+import androidx.core.content.edit
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.preferences.theme.Background
import de.jrpie.android.launcher.preferences.theme.ColorTheme
-import androidx.core.content.edit
private fun migrateStringPreference(
@@ -392,5 +392,5 @@ fun migratePreferencesFromVersionUnknown(context: Context) {
LauncherPreferences.internal().versionCode(1)
Log.i(TAG, "migrated preferences to version 1.")
- migratePreferencesFromVersion1()
+ migratePreferencesFromVersion1(context)
}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/serialization/PreferenceSerializers.kt b/app/src/main/java/de/jrpie/android/launcher/preferences/serialization/PreferenceSerializers.kt
index a2749ae..7b5d794 100644
--- a/app/src/main/java/de/jrpie/android/launcher/preferences/serialization/PreferenceSerializers.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/preferences/serialization/PreferenceSerializers.kt
@@ -5,6 +5,7 @@ package de.jrpie.android.launcher.preferences.serialization
import de.jrpie.android.launcher.apps.AbstractAppInfo
import de.jrpie.android.launcher.apps.PinnedShortcutInfo
import de.jrpie.android.launcher.widgets.Widget
+import de.jrpie.android.launcher.widgets.WidgetPanel
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializationException
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializer
import kotlinx.serialization.Serializable
@@ -46,6 +47,22 @@ class SetWidgetSerializer :
}
}
+@Suppress("UNCHECKED_CAST")
+class SetWidgetPanelSerializer :
+ PreferenceSerializer?, java.util.Set?> {
+ @Throws(PreferenceSerializationException::class)
+ override fun serialize(value: java.util.Set?): java.util.Set? {
+ return value?.map(WidgetPanel::serialize)
+ ?.toHashSet() as? java.util.Set
+ }
+
+ @Throws(PreferenceSerializationException::class)
+ override fun deserialize(value: java.util.Set?): java.util.Set? {
+ return value?.map(java.lang.String::toString)?.map(WidgetPanel::deserialize)
+ ?.toHashSet() as? java.util.Set
+ }
+}
+
@Suppress("UNCHECKED_CAST")
class SetPinnedShortcutInfoPreferenceSerializer :
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt b/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt
index 3f1e497..192a8e9 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/HomeActivity.kt
@@ -207,7 +207,6 @@ class HomeActivity : UIObject, Activity() {
}
override fun onTouchEvent(event: MotionEvent): Boolean {
- android.util.Log.e("Launcher", "on touch")
touchGestureDetector?.onTouchEvent(event)
return true
}
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/list/other/OtherRecyclerAdapter.kt b/app/src/main/java/de/jrpie/android/launcher/ui/list/other/OtherRecyclerAdapter.kt
index f176469..06be78a 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/list/other/OtherRecyclerAdapter.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/list/other/OtherRecyclerAdapter.kt
@@ -11,6 +11,7 @@ import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.actions.LauncherAction
+import de.jrpie.android.launcher.actions.WidgetPanelAction
import de.jrpie.android.launcher.ui.list.ListActivity
/**
@@ -23,8 +24,10 @@ import de.jrpie.android.launcher.ui.list.ListActivity
class OtherRecyclerAdapter(val activity: Activity) :
RecyclerView.Adapter() {
- private val othersList: Array =
- LauncherAction.entries.filter { it.isAvailable(activity) }.toTypedArray()
+ private val othersList: Array =
+ LauncherAction.entries.filter { it.isAvailable(activity) }
+ .plus(WidgetPanelAction(-1))
+ .toTypedArray()
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
View.OnClickListener {
@@ -36,10 +39,12 @@ class OtherRecyclerAdapter(val activity: Activity) :
val pos = bindingAdapterPosition
val content = othersList[pos]
- activity.finish()
val gestureId = (activity as? ListActivity)?.forGesture ?: return
val gesture = Gesture.byId(gestureId) ?: return
- Action.setActionForGesture(gesture, content)
+ content.showConfigurationDialog(activity) { configuredAction ->
+ Action.setActionForGesture(gesture, configuredAction)
+ activity.finish()
+ }
}
init {
@@ -48,11 +53,11 @@ class OtherRecyclerAdapter(val activity: Activity) :
}
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
- val otherLabel = activity.getString(othersList[i].label)
- val icon = othersList[i].icon
+ val otherLabel = othersList[i].label(activity)
+ val icon = othersList[i].getIcon(activity)
viewHolder.textView.text = otherLabel
- viewHolder.iconView.setImageResource(icon)
+ viewHolder.iconView.setImageDrawable(icon)
}
override fun getItemCount(): Int {
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt b/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt
index 8907f04..bb9df74 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt
@@ -11,6 +11,7 @@ import de.jrpie.android.launcher.actions.openAppsList
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.preferences.theme.ColorTheme
import de.jrpie.android.launcher.setDefaultHomeScreen
+import de.jrpie.android.launcher.ui.widgets.manage.ManageWidgetPanelsActivity
import de.jrpie.android.launcher.ui.widgets.manage.ManageWidgetsActivity
@@ -90,6 +91,14 @@ class SettingsFragmentLauncher : PreferenceFragmentCompat() {
true
}
+ val manageWidgetPanels = findPreference(
+ LauncherPreferences.widgets().keys().customPanels()
+ )
+ manageWidgetPanels?.setOnPreferenceClickListener {
+ startActivity(Intent(requireActivity(), ManageWidgetPanelsActivity::class.java))
+ true
+ }
+
val hiddenApps = findPreference(
LauncherPreferences.apps().keys().hidden()
)
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/WidgetContainerView.kt b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/WidgetContainerView.kt
index 04668ca..d071771 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/WidgetContainerView.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/WidgetContainerView.kt
@@ -13,6 +13,7 @@ import android.view.ViewGroup
import androidx.core.graphics.contains
import androidx.core.view.size
import de.jrpie.android.launcher.widgets.Widget
+import de.jrpie.android.launcher.widgets.WidgetPanel
import de.jrpie.android.launcher.widgets.WidgetPosition
import kotlin.math.max
@@ -20,22 +21,29 @@ import kotlin.math.max
/**
* This only works in an Activity, not AppCompatActivity
*/
-open class WidgetContainerView(context: Context, attrs: AttributeSet? = null) : ViewGroup(context, attrs) {
+open class WidgetContainerView(
+ var widgetPanelId: Int,
+ context: Context,
+ attrs: AttributeSet? = null
+) : ViewGroup(context, attrs) {
+ constructor(context: Context, attrs: AttributeSet) : this(WidgetPanel.HOME.id, context, attrs)
var widgetViewById = HashMap()
- open fun updateWidgets(activity: Activity, widgets: Set?) {
- if (widgets == null) {
- return
- }
- Log.i("WidgetContainer", "updating ${activity.localClassName}")
- widgetViewById.clear()
- (0..
+ open fun updateWidgets(activity: Activity, widgets: Collection?) {
+ synchronized(widgetViewById) {
+ if (widgets == null) {
+ return
+ }
+ Log.i("WidgetContainer", "updating ${activity.localClassName}")
+ widgetViewById.forEach { removeView(it.value) }
+ widgetViewById.clear()
+ widgets.filter { it.panelId == widgetPanelId }.forEach { widget ->
widget.createView(activity)?.let {
- addView(it, WidgetContainerView.Companion.LayoutParams(widget.position))
+ addView(it, LayoutParams(widget.position))
widgetViewById.put(widget.id, it)
}
+ }
}
}
@@ -67,7 +75,6 @@ open class WidgetContainerView(context: Context, attrs: AttributeSet? = null) :
(0...onCreate(savedInstanceState)
+ super.onCreate()
+ widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.Companion.HOME.id)
+ val binding = ActivityWidgetPanelBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ binding.widgetPanelWidgetContainer.widgetPanelId = widgetPanelId
+ binding.widgetPanelWidgetContainer.updateWidgets(
+ this,
+ LauncherPreferences.widgets().widgets()
+ )
+ }
+
+ override fun getTheme(): Resources.Theme {
+ val mTheme = modifyTheme(super.getTheme())
+ mTheme.applyStyle(R.style.backgroundWallpaper, true)
+ LauncherPreferences.clock().font().applyToTheme(mTheme)
+ LauncherPreferences.theme().colorTheme().applyToTheme(
+ mTheme,
+ LauncherPreferences.theme().textShadow()
+ )
+
+ return mTheme
+ }
+
+
+ override fun onStart() {
+ super.onStart()
+ super.onStart()
+ }
+
+ override fun isHomeScreen(): Boolean {
+ return true
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/ManageWidgetPanelsActivity.kt b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/ManageWidgetPanelsActivity.kt
new file mode 100644
index 0000000..b18852f
--- /dev/null
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/ManageWidgetPanelsActivity.kt
@@ -0,0 +1,104 @@
+package de.jrpie.android.launcher.ui.widgets.manage
+
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.content.SharedPreferences
+import android.content.res.Resources
+import android.os.Bundle
+import android.widget.EditText
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import de.jrpie.android.launcher.R
+import de.jrpie.android.launcher.databinding.ActivityManageWidgetPanelsBinding
+import de.jrpie.android.launcher.preferences.LauncherPreferences
+import de.jrpie.android.launcher.ui.UIObject
+import de.jrpie.android.launcher.widgets.WidgetPanel
+import de.jrpie.android.launcher.widgets.updateWidgetPanel
+
+class ManageWidgetPanelsActivity : AppCompatActivity(), UIObject {
+
+ private val sharedPreferencesListener =
+ SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey ->
+ if (prefKey == LauncherPreferences.widgets().keys().customPanels()) {
+ viewAdapter.widgetPanels =
+ (LauncherPreferences.widgets().customPanels() ?: setOf()).toTypedArray()
+
+ @SuppressLint("NotifyDataSetChanged")
+ viewAdapter.notifyDataSetChanged()
+ }
+ }
+ private lateinit var binding: ActivityManageWidgetPanelsBinding
+ private lateinit var viewAdapter: WidgetPanelsRecyclerAdapter
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ super.onCreate()
+
+ binding = ActivityManageWidgetPanelsBinding.inflate(layoutInflater)
+ setContentView(binding.main)
+
+ val viewManager = LinearLayoutManager(this)
+ viewAdapter = WidgetPanelsRecyclerAdapter(this, true) { widgetPanel ->
+ startActivity(
+ Intent(
+ this@ManageWidgetPanelsActivity,
+ ManageWidgetsActivity::class.java
+ ).also {
+ it.putExtra(EXTRA_PANEL_ID, widgetPanel.id)
+ })
+ }
+ binding.manageWidgetPanelsRecycler.apply {
+ // improve performance (since content changes don't change the layout size)
+ setHasFixedSize(true)
+ layoutManager = viewManager
+ adapter = viewAdapter
+ }
+ binding.manageWidgetPanelsClose.setOnClickListener { finish() }
+ binding.manageWidgetPanelsAddPanel.setOnClickListener {
+ AlertDialog.Builder(this@ManageWidgetPanelsActivity, R.style.AlertDialogCustom).apply {
+ setTitle(R.string.dialog_create_widget_panel_title)
+ setNegativeButton(R.string.dialog_cancel) { _, _ -> }
+ setPositiveButton(R.string.dialog_ok) { dialogInterface, _ ->
+ val panelId = WidgetPanel.allocateId()
+ val label = (dialogInterface as? AlertDialog)
+ ?.findViewById(R.id.dialog_create_widget_panel_edit_text)?.text?.toString()
+ ?: (getString(R.string.widget_panel_default_name, panelId))
+
+ updateWidgetPanel(WidgetPanel(panelId, label))
+ }
+ setView(R.layout.dialog_create_widget_panel)
+ }.create().also { it.show() }.apply {
+ findViewById(R.id.dialog_create_widget_panel_edit_text)
+ ?.setText(
+ getString(
+ R.string.widget_panel_default_name,
+ WidgetPanel.allocateId()
+ )
+ )
+ }
+ true
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ super.onStart()
+ LauncherPreferences.getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
+ }
+
+ override fun onPause() {
+ LauncherPreferences.getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener)
+ super.onPause()
+ }
+
+ override fun getTheme(): Resources.Theme {
+ return modifyTheme(super.getTheme())
+ }
+
+ override fun setOnClicks() {
+ binding.manageWidgetPanelsClose.setOnClickListener { finish() }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/ManageWidgetsActivity.kt b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/ManageWidgetsActivity.kt
index 3172401..d191b70 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/ManageWidgetsActivity.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/ManageWidgetsActivity.kt
@@ -2,7 +2,6 @@ package de.jrpie.android.launcher.ui.widgets.manage
import android.app.Activity
import android.appwidget.AppWidgetManager
-import android.appwidget.AppWidgetProviderInfo
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Resources
@@ -18,6 +17,7 @@ import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.ui.UIObject
import de.jrpie.android.launcher.ui.widgets.WidgetContainerView
import de.jrpie.android.launcher.widgets.AppWidget
+import de.jrpie.android.launcher.widgets.WidgetPanel
import de.jrpie.android.launcher.widgets.WidgetPosition
import kotlin.math.min
@@ -27,9 +27,13 @@ import kotlin.math.min
const val REQUEST_CREATE_APPWIDGET = 1
const val REQUEST_PICK_APPWIDGET = 2
+const val EXTRA_PANEL_ID = "widgetPanelId"
+
// We can't use AppCompatActivity, since some AppWidgets don't work there.
class ManageWidgetsActivity : Activity(), UIObject {
+ var panelId: Int = WidgetPanel.HOME.id
+
private var sharedPreferencesListener =
SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey ->
if (prefKey == LauncherPreferences.widgets().keys().widgets()) {
@@ -44,6 +48,9 @@ class ManageWidgetsActivity : Activity(), UIObject {
super.onCreate(savedInstanceState)
super.onCreate()
setContentView(R.layout.activity_manage_widgets)
+
+ panelId = intent.extras?.getInt(EXTRA_PANEL_ID, WidgetPanel.HOME.id) ?: WidgetPanel.HOME.id
+
findViewById(R.id.manage_widgets_button_add).setOnClickListener {
selectWidget()
}
@@ -54,9 +61,10 @@ class ManageWidgetsActivity : Activity(), UIObject {
insets
}
- findViewById(R.id.manage_widgets_container).updateWidgets(this,
- (application as Application).widgets.value
- )
+ findViewById(R.id.manage_widgets_container).let {
+ it.widgetPanelId = panelId
+ it.updateWidgets(this, (application as Application).widgets.value)
+ }
}
override fun onStart() {
@@ -101,6 +109,10 @@ class ManageWidgetsActivity : Activity(), UIObject {
AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetHost.allocateAppWidgetId()
)
+ it.putExtra(
+ EXTRA_PANEL_ID,
+ panelId
+ )
}, REQUEST_PICK_APPWIDGET
)
}
@@ -124,7 +136,7 @@ class ManageWidgetsActivity : Activity(), UIObject {
display.height
)
- val widget = AppWidget(appWidgetId, provider, position)
+ val widget = AppWidget(appWidgetId, position, panelId, provider)
LauncherPreferences.widgets().widgets(
(LauncherPreferences.widgets().widgets() ?: HashSet()).also {
it.add(widget)
@@ -135,7 +147,7 @@ class ManageWidgetsActivity : Activity(), UIObject {
private fun configureWidget(data: Intent) {
val extras = data.extras
val appWidgetId = extras!!.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
- val widget = AppWidget(appWidgetId)
+ val widget = AppWidget(appWidgetId, panelId = panelId)
if (widget.isConfigurable(this)) {
widget.configure(this, REQUEST_CREATE_APPWIDGET)
} else {
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/SelectWidgetActivity.kt b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/SelectWidgetActivity.kt
index c414db6..a1bd3b5 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/SelectWidgetActivity.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/SelectWidgetActivity.kt
@@ -21,6 +21,7 @@ import de.jrpie.android.launcher.widgets.ClockWidget
import de.jrpie.android.launcher.widgets.LauncherAppWidgetProvider
import de.jrpie.android.launcher.widgets.LauncherClockWidgetProvider
import de.jrpie.android.launcher.widgets.LauncherWidgetProvider
+import de.jrpie.android.launcher.widgets.WidgetPanel
import de.jrpie.android.launcher.widgets.WidgetPosition
import de.jrpie.android.launcher.widgets.bindAppWidgetOrRequestPermission
import de.jrpie.android.launcher.widgets.getAppWidgetHost
@@ -38,6 +39,7 @@ private const val REQUEST_WIDGET_PERMISSION = 29
class SelectWidgetActivity : AppCompatActivity(), UIObject {
lateinit var binding: ActivitySelectWidgetBinding
var widgetId: Int = -1
+ var widgetPanelId: Int = WidgetPanel.HOME.id
private fun tryBindWidget(info: LauncherWidgetProvider) {
when (info) {
@@ -53,13 +55,14 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject {
RESULT_OK,
Intent().also {
it.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
+ it.putExtra(EXTRA_PANEL_ID, widgetPanelId)
}
)
finish()
}
}
is LauncherClockWidgetProvider -> {
- updateWidget(ClockWidget(widgetId, WidgetPosition(0,4,12,3)))
+ updateWidget(ClockWidget(widgetId, WidgetPosition(0, 4, 12, 3), widgetPanelId))
finish()
}
}
@@ -79,6 +82,7 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject {
widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
+ widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
if (widgetId == -1) {
widgetId = getAppWidgetHost().allocateAppWidgetId()
}
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetManagerView.kt b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetManagerView.kt
index 2d41e13..7a355f7 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetManagerView.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetManagerView.kt
@@ -20,6 +20,7 @@ import androidx.core.graphics.toRect
import androidx.core.view.children
import de.jrpie.android.launcher.ui.widgets.WidgetContainerView
import de.jrpie.android.launcher.widgets.Widget
+import de.jrpie.android.launcher.widgets.WidgetPanel
import de.jrpie.android.launcher.widgets.WidgetPosition
import de.jrpie.android.launcher.widgets.updateWidget
import kotlin.math.max
@@ -28,8 +29,9 @@ import kotlin.math.min
/**
* A variant of the [WidgetContainerView] which allows to manage widgets.
*/
-class WidgetManagerView(context: Context, attrs: AttributeSet? = null) :
- WidgetContainerView(context, attrs) {
+class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSet? = null) :
+ WidgetContainerView(widgetPanelId, context, attrs) {
+ constructor(context: Context, attrs: AttributeSet?) : this(WidgetPanel.HOME.id, context, attrs)
val TOUCH_SLOP: Int
val TOUCH_SLOP_SQUARE: Int
@@ -155,13 +157,14 @@ class WidgetManagerView(context: Context, attrs: AttributeSet? = null) :
selectedWidgetOverlayView?.mode = null
}
- override fun updateWidgets(activity: Activity, widgets: Set?) {
+ override fun updateWidgets(activity: Activity, widgets: Collection?) {
super.updateWidgets(activity, widgets)
if (widgets == null) {
return
}
+ children.mapNotNull { it as? WidgetOverlayView }.forEach { removeView(it) }
- widgets.forEach { widget ->
+ widgets.filter { it.panelId == widgetPanelId }.forEach { widget ->
WidgetOverlayView(activity).let {
addView(it)
it.widgetId = widget.id
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetOverlayView.kt b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetOverlayView.kt
index 0ce789f..1b8a2d2 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetOverlayView.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetOverlayView.kt
@@ -13,12 +13,13 @@ import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.widgets.Widget
import de.jrpie.android.launcher.widgets.updateWidget
-/**
- * An overlay to show configuration options for a widget.
- */
private const val HANDLE_SIZE = 100
private const val HANDLE_EDGE_SIZE = (1.2 * HANDLE_SIZE).toInt()
+
+/**
+ * An overlay to show configuration options for a widget in [WidgetManagerView]
+ */
class WidgetOverlayView : View {
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetPanelsRecyclerAdapter.kt b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetPanelsRecyclerAdapter.kt
new file mode 100644
index 0000000..40c2c2f
--- /dev/null
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/WidgetPanelsRecyclerAdapter.kt
@@ -0,0 +1,98 @@
+package de.jrpie.android.launcher.ui.widgets.manage
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.EditText
+import android.widget.PopupMenu
+import android.widget.TextView
+import androidx.appcompat.app.AlertDialog
+import androidx.recyclerview.widget.RecyclerView
+import de.jrpie.android.launcher.R
+import de.jrpie.android.launcher.preferences.LauncherPreferences
+import de.jrpie.android.launcher.widgets.WidgetPanel
+import de.jrpie.android.launcher.widgets.updateWidgetPanel
+
+
+class WidgetPanelsRecyclerAdapter(
+ val context: Context,
+ val showMenu: Boolean = false,
+ val onSelectWidgetPanel: (WidgetPanel) -> Unit
+) :
+ RecyclerView.Adapter() {
+
+ var widgetPanels = (LauncherPreferences.widgets().customPanels() ?: setOf()).toTypedArray()
+
+ class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ var labelView: TextView = itemView.findViewById(R.id.list_widget_panels_label)
+ }
+
+ override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
+ viewHolder.labelView.text = widgetPanels[i].label
+
+ viewHolder.itemView.setOnClickListener {
+ onSelectWidgetPanel(widgetPanels[i])
+ }
+
+ if (showMenu) {
+ viewHolder.itemView.setOnLongClickListener {
+ showOptionsPopup(
+ viewHolder,
+ widgetPanels[i]
+ )
+ }
+ }
+ }
+
+ @Suppress("SameReturnValue")
+ private fun showOptionsPopup(
+ viewHolder: ViewHolder,
+ widgetPanel: WidgetPanel
+ ): Boolean {
+ //create the popup menu
+
+ val popup = PopupMenu(context, viewHolder.labelView)
+ popup.menu.add(R.string.manage_widget_panels_delete).setOnMenuItemClickListener { _ ->
+ widgetPanel.delete(context)
+ true
+ }
+ popup.menu.add(R.string.manage_widget_panels_rename).setOnMenuItemClickListener { _ ->
+ AlertDialog.Builder(context, R.style.AlertDialogCustom).apply {
+ setNegativeButton(R.string.dialog_cancel) { _, _ -> }
+ setPositiveButton(R.string.dialog_ok) { dialogInterface, _ ->
+ var newLabel = (dialogInterface as? AlertDialog)
+ ?.findViewById(R.id.dialog_rename_widget_panel_edit_text)
+ ?.text?.toString()
+ if (newLabel == null || newLabel.isEmpty()) {
+ newLabel =
+ (context.getString(R.string.widget_panel_default_name, widgetPanel.id))
+ }
+ widgetPanel.label = newLabel
+ updateWidgetPanel(widgetPanel)
+ }
+ setView(R.layout.dialog_rename_widget_panel)
+ }.create().also { it.show() }.apply {
+ findViewById(R.id.dialog_rename_widget_panel_edit_text)?.let {
+ it.setText(widgetPanel.label)
+ it.hint = context.getString(R.string.widget_panel_default_name, widgetPanel.id)
+ }
+ }
+ true
+ }
+
+ popup.show()
+ return true
+ }
+
+ override fun getItemCount(): Int {
+ return widgetPanels.size
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val view: View =
+ LayoutInflater.from(context).inflate(R.layout.list_widget_panels_row, parent, false)
+ val viewHolder = ViewHolder(view)
+ return viewHolder
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/widgets/AppWidget.kt b/app/src/main/java/de/jrpie/android/launcher/widgets/AppWidget.kt
index 3e9a2eb..22a63eb 100644
--- a/app/src/main/java/de/jrpie/android/launcher/widgets/AppWidget.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/widgets/AppWidget.kt
@@ -1,4 +1,4 @@
-package de.jrpie.android.launcher.widgets;
+package de.jrpie.android.launcher.widgets
import android.app.Activity
import android.appwidget.AppWidgetHostView
@@ -11,7 +11,6 @@ import android.util.DisplayMetrics
import android.util.SizeF
import android.view.View
import de.jrpie.android.launcher.Application
-import de.jrpie.android.launcher.ui.HomeActivity
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -20,6 +19,7 @@ import kotlinx.serialization.Serializable
class AppWidget(
override val id: Int,
override var position: WidgetPosition = WidgetPosition(0,0,1,1),
+ override var panelId: Int = WidgetPanel.HOME.id,
override var allowInteraction: Boolean = false,
// We keep track of packageName, className and user to make it possible to restore the widget
@@ -31,10 +31,16 @@ class AppWidget(
): Widget() {
- constructor(id: Int, widgetProviderInfo: AppWidgetProviderInfo, position: WidgetPosition) :
+ constructor(
+ id: Int,
+ position: WidgetPosition,
+ panelId: Int,
+ widgetProviderInfo: AppWidgetProviderInfo
+ ) :
this(
id,
position,
+ panelId,
false,
widgetProviderInfo.provider.packageName,
widgetProviderInfo.provider.className,
diff --git a/app/src/main/java/de/jrpie/android/launcher/widgets/ClockWidget.kt b/app/src/main/java/de/jrpie/android/launcher/widgets/ClockWidget.kt
index d819538..d0d1c0e 100644
--- a/app/src/main/java/de/jrpie/android/launcher/widgets/ClockWidget.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/widgets/ClockWidget.kt
@@ -14,6 +14,7 @@ import kotlinx.serialization.Serializable
class ClockWidget(
override val id: Int,
override var position: WidgetPosition,
+ override val panelId: Int,
override var allowInteraction: Boolean = true
) : Widget() {
diff --git a/app/src/main/java/de/jrpie/android/launcher/widgets/Widget.kt b/app/src/main/java/de/jrpie/android/launcher/widgets/Widget.kt
index d3610dd..dbe667b 100644
--- a/app/src/main/java/de/jrpie/android/launcher/widgets/Widget.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/widgets/Widget.kt
@@ -14,6 +14,7 @@ import kotlinx.serialization.json.Json
sealed class Widget {
abstract val id: Int
abstract var position: WidgetPosition
+ abstract val panelId: Int
abstract var allowInteraction: Boolean
/**
@@ -36,6 +37,10 @@ sealed class Widget {
)
}
+ fun getPanel(): WidgetPanel? {
+ return WidgetPanel.byId(panelId)
+ }
+
override fun hashCode(): Int {
return id
}
diff --git a/app/src/main/java/de/jrpie/android/launcher/widgets/WidgetPanel.kt b/app/src/main/java/de/jrpie/android/launcher/widgets/WidgetPanel.kt
new file mode 100644
index 0000000..93e588d
--- /dev/null
+++ b/app/src/main/java/de/jrpie/android/launcher/widgets/WidgetPanel.kt
@@ -0,0 +1,58 @@
+package de.jrpie.android.launcher.widgets
+
+import android.content.Context
+import de.jrpie.android.launcher.preferences.LauncherPreferences
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
+
+
+@Serializable
+@SerialName("panel")
+class WidgetPanel(val id: Int, var label: String) {
+
+ override fun equals(other: Any?): Boolean {
+ return (other as? WidgetPanel)?.id == id
+ }
+
+ override fun hashCode(): Int {
+ return id
+ }
+
+ fun serialize(): String {
+ return Json.encodeToString(this)
+ }
+
+ fun delete(context: Context) {
+ LauncherPreferences.widgets().customPanels(
+ (LauncherPreferences.widgets().customPanels() ?: setOf()).minus(this)
+ )
+ (LauncherPreferences.widgets().widgets() ?: return)
+ .filter { it.panelId == this.id }.forEach { it.delete(context) }
+ }
+
+
+ companion object {
+ val HOME = WidgetPanel(0, "home")
+ fun byId(id: Int): WidgetPanel? {
+ if (id == 0) {
+ return HOME
+ }
+ return LauncherPreferences.widgets().customPanels()?.firstOrNull { it.id == id }
+ }
+
+ fun allocateId(): Int {
+ return (
+ (LauncherPreferences.widgets().customPanels() ?: setOf())
+ .plus(HOME)
+ .maxOfOrNull { it.id } ?: 0
+ ) + 1
+ }
+
+ fun deserialize(serialized: String): WidgetPanel {
+ return Json.decodeFromString(serialized)
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/widgets/Widgets.kt b/app/src/main/java/de/jrpie/android/launcher/widgets/Widgets.kt
index cd4ef29..b7f140b 100644
--- a/app/src/main/java/de/jrpie/android/launcher/widgets/Widgets.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/widgets/Widgets.kt
@@ -74,9 +74,19 @@ fun getAppWidgetProviders( context: Context ): List {
fun updateWidget(widget: Widget) {
- var widgets = LauncherPreferences.widgets().widgets() ?: setOf()
- widgets = widgets.minus(widget).plus(widget)
- LauncherPreferences.widgets().widgets(widgets)
+ LauncherPreferences.widgets().widgets(
+ (LauncherPreferences.widgets().widgets() ?: setOf())
+ .minus(widget)
+ .plus(widget)
+ )
+}
+
+fun updateWidgetPanel(widgetPanel: WidgetPanel) {
+ LauncherPreferences.widgets().customPanels(
+ (LauncherPreferences.widgets().customPanels() ?: setOf())
+ .minus(widgetPanel)
+ .plus(widgetPanel)
+ )
}
fun Context.getAppWidgetHost(): AppWidgetHost {
diff --git a/app/src/main/res/drawable/baseline_widgets_24.xml b/app/src/main/res/drawable/baseline_widgets_24.xml
new file mode 100644
index 0000000..fd0f571
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_widgets_24.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_manage_widget_panels.xml b/app/src/main/res/layout/activity_manage_widget_panels.xml
new file mode 100644
index 0000000..60413a3
--- /dev/null
+++ b/app/src/main/res/layout/activity_manage_widget_panels.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_widget_panel.xml b/app/src/main/res/layout/activity_widget_panel.xml
new file mode 100644
index 0000000..6ef6b20
--- /dev/null
+++ b/app/src/main/res/layout/activity_widget_panel.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_create_widget_panel.xml b/app/src/main/res/layout/dialog_create_widget_panel.xml
new file mode 100644
index 0000000..900823d
--- /dev/null
+++ b/app/src/main/res/layout/dialog_create_widget_panel.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_rename_widget_panel.xml b/app/src/main/res/layout/dialog_rename_widget_panel.xml
new file mode 100644
index 0000000..effb783
--- /dev/null
+++ b/app/src/main/res/layout/dialog_rename_widget_panel.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_select_widget_panel.xml b/app/src/main/res/layout/dialog_select_widget_panel.xml
new file mode 100644
index 0000000..5f83d51
--- /dev/null
+++ b/app/src/main/res/layout/dialog_select_widget_panel.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_widget_panels_row.xml b/app/src/main/res/layout/list_widget_panels_row.xml
new file mode 100644
index 0000000..53f7449
--- /dev/null
+++ b/app/src/main/res/layout/list_widget_panels_row.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index 30e4cda..f783d2a 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -10,6 +10,7 @@
internal.first_startup
internal.version_code
widgets.widgets
+ widgets.custom_panels
apps.favorites
apps.hidden
apps.pinned_shortcuts
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a8813f8..7d29128 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -99,6 +99,7 @@
Click on time
Manage widgets
+ Manage widget panels
Choose App
@@ -400,5 +401,23 @@
Clock
The default clock of μLauncher
+ Delete
+ Rename
+
+ Widget Panel #%1$d
+
+ - Contains %d widget.
+ - Contains %d widgets.
+
+
+
+ Ok
+ Widget Panels
+ Select a Widget Panel
+ Create new widget panel
+ Launcher > Manage Widget Panels.]]>
+ Open Widget Panel
+ This widget panel no longer exists.
+ Widgets
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index b4bc5f0..0ee7c17 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -3,14 +3,10 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
+
-
-
-
+
+
+
+