mirror of
https://github.com/jrpie/Launcher.git
synced 2025-04-28 06:40:56 +02:00
This commit is contained in:
parent
ffaaba7abb
commit
a4fcdf60c7
37 changed files with 807 additions and 68 deletions
|
@ -21,9 +21,15 @@
|
||||||
android:theme="@style/launcherBaseTheme"
|
android:theme="@style/launcherBaseTheme"
|
||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="UnusedAttribute">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.widgets.manage.ManageWidgetsActivity"
|
android:name=".ui.widgets.manage.ManageWidgetPanelsActivity"
|
||||||
android:theme="@style/launcherHomeTheme"
|
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".ui.widgets.WidgetPanelActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".ui.widgets.manage.ManageWidgetsActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/launcherHomeTheme" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.widgets.manage.SelectWidgetActivity"
|
android:name=".ui.widgets.manage.SelectWidgetActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
|
@ -6,14 +6,18 @@ import android.content.SharedPreferences.Editor
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.edit
|
||||||
import de.jrpie.android.launcher.R
|
import de.jrpie.android.launcher.R
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
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
|
@Serializable
|
||||||
sealed interface Action {
|
sealed interface Action {
|
||||||
fun invoke(context: Context, rect: Rect? = null): Boolean
|
fun invoke(context: Context, rect: Rect? = null): Boolean
|
||||||
|
@ -21,6 +25,10 @@ sealed interface Action {
|
||||||
fun getIcon(context: Context): Drawable?
|
fun getIcon(context: Context): Drawable?
|
||||||
fun isAvailable(context: Context): Boolean
|
fun isAvailable(context: Context): Boolean
|
||||||
|
|
||||||
|
fun showConfigurationDialog(context: Context, onSuccess: (Action) -> Unit) {
|
||||||
|
onSuccess(this)
|
||||||
|
}
|
||||||
|
|
||||||
// Can the action be used to reach µLauncher settings?
|
// Can the action be used to reach µLauncher settings?
|
||||||
fun canReachSettings(): Boolean
|
fun canReachSettings(): Boolean
|
||||||
|
|
||||||
|
|
|
@ -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<TextView>(R.id.dialog_select_widget_panel_info)
|
||||||
|
alertDialog.findViewById<RecyclerView>(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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.MapAbstractAppInfoStringPreferenceSerializer;
|
||||||
import de.jrpie.android.launcher.preferences.serialization.SetAbstractAppInfoPreferenceSerializer;
|
import de.jrpie.android.launcher.preferences.serialization.SetAbstractAppInfoPreferenceSerializer;
|
||||||
import de.jrpie.android.launcher.preferences.serialization.SetPinnedShortcutInfoPreferenceSerializer;
|
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.serialization.SetWidgetSerializer;
|
||||||
import de.jrpie.android.launcher.preferences.theme.Background;
|
import de.jrpie.android.launcher.preferences.theme.Background;
|
||||||
import de.jrpie.android.launcher.preferences.theme.ColorTheme;
|
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"),
|
@Preference(name = "lock_method", type = LockMethod.class, defaultValue = "DEVICE_ADMIN"),
|
||||||
}),
|
}),
|
||||||
@PreferenceGroup(name = "widgets", prefix = "settings_widgets_", suffix= "_key", value = {
|
@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 {}
|
public final class LauncherPreferences$Config {}
|
|
@ -2,18 +2,21 @@ package de.jrpie.android.launcher.preferences
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import de.jrpie.android.launcher.Application
|
||||||
import de.jrpie.android.launcher.BuildConfig
|
import de.jrpie.android.launcher.BuildConfig
|
||||||
import de.jrpie.android.launcher.actions.Action
|
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
|
||||||
import de.jrpie.android.launcher.apps.AbstractAppInfo.Companion.INVALID_USER
|
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.apps.DetailedAppInfo
|
||||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion1
|
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion1
|
||||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion2
|
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion2
|
||||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion3
|
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.preferences.legacy.migratePreferencesFromVersionUnknown
|
||||||
import de.jrpie.android.launcher.ui.HomeActivity
|
import de.jrpie.android.launcher.ui.HomeActivity
|
||||||
import de.jrpie.android.launcher.widgets.ClockWidget
|
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.WidgetPosition
|
||||||
import de.jrpie.android.launcher.widgets.deleteAllWidgets
|
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
|
* Increase when breaking changes are introduced and write an appropriate case in
|
||||||
* `migratePreferencesToNewVersion`
|
* `migratePreferencesToNewVersion`
|
||||||
*/
|
*/
|
||||||
const val PREFERENCE_VERSION = 4
|
const val PREFERENCE_VERSION = 5
|
||||||
const val UNKNOWN_PREFERENCE_VERSION = -1
|
const val UNKNOWN_PREFERENCE_VERSION = -1
|
||||||
private const val TAG = "Launcher - Preferences"
|
private const val TAG = "Launcher - Preferences"
|
||||||
|
|
||||||
|
@ -43,18 +46,23 @@ fun migratePreferencesToNewVersion(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
1 -> {
|
1 -> {
|
||||||
migratePreferencesFromVersion1()
|
migratePreferencesFromVersion1(context)
|
||||||
Log.i(TAG, "migration of preferences complete (1 -> ${PREFERENCE_VERSION}).")
|
Log.i(TAG, "migration of preferences complete (1 -> ${PREFERENCE_VERSION}).")
|
||||||
}
|
}
|
||||||
2 -> {
|
2 -> {
|
||||||
migratePreferencesFromVersion2()
|
migratePreferencesFromVersion2(context)
|
||||||
Log.i(TAG, "migration of preferences complete (2 -> ${PREFERENCE_VERSION}).")
|
Log.i(TAG, "migration of preferences complete (2 -> ${PREFERENCE_VERSION}).")
|
||||||
}
|
}
|
||||||
3 -> {
|
3 -> {
|
||||||
migratePreferencesFromVersion3()
|
migratePreferencesFromVersion3(context)
|
||||||
Log.i(TAG, "migration of preferences complete (3 -> ${PREFERENCE_VERSION}).")
|
Log.i(TAG, "migration of preferences complete (3 -> ${PREFERENCE_VERSION}).")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4 -> {
|
||||||
|
migratePreferencesFromVersion4(context)
|
||||||
|
Log.i(TAG, "migration of preferences complete (4 -> ${PREFERENCE_VERSION}).")
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
Log.w(
|
Log.w(
|
||||||
TAG,
|
TAG,
|
||||||
|
@ -78,7 +86,11 @@ fun resetPreferences(context: Context) {
|
||||||
|
|
||||||
LauncherPreferences.widgets().widgets(
|
LauncherPreferences.widgets().widgets(
|
||||||
setOf(
|
setOf(
|
||||||
ClockWidget(-500, WidgetPosition(1,4,10,3))
|
ClockWidget(
|
||||||
|
(context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
|
||||||
|
WidgetPosition(1, 3, 10, 4),
|
||||||
|
WidgetPanel.HOME.id
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package de.jrpie.android.launcher.preferences.legacy
|
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.Action
|
||||||
import de.jrpie.android.launcher.actions.AppAction
|
import de.jrpie.android.launcher.actions.AppAction
|
||||||
import de.jrpie.android.launcher.actions.Gesture
|
import de.jrpie.android.launcher.actions.Gesture
|
||||||
import de.jrpie.android.launcher.actions.LauncherAction
|
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.AbstractAppInfo.Companion.INVALID_USER
|
||||||
|
import de.jrpie.android.launcher.apps.AppInfo
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
|
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
@ -13,7 +15,6 @@ import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import androidx.core.content.edit
|
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@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
|
* Migrate preferences from version 1 (used until version j-0.0.18) to the current format
|
||||||
* (see [PREFERENCE_VERSION])
|
* (see [PREFERENCE_VERSION])
|
||||||
*/
|
*/
|
||||||
fun migratePreferencesFromVersion1() {
|
fun migratePreferencesFromVersion1(context: Context) {
|
||||||
assert(LauncherPreferences.internal().versionCode() == 1)
|
assert(LauncherPreferences.internal().versionCode() == 1)
|
||||||
Gesture.entries.forEach { g -> migrateAction(g.id) }
|
Gesture.entries.forEach { g -> migrateAction(g.id) }
|
||||||
migrateAppInfoSet(LauncherPreferences.apps().keys().hidden())
|
migrateAppInfoSet(LauncherPreferences.apps().keys().hidden())
|
||||||
|
@ -137,5 +138,5 @@ fun migratePreferencesFromVersion1() {
|
||||||
migrateAppInfoStringMap(LauncherPreferences.apps().keys().customNames())
|
migrateAppInfoStringMap(LauncherPreferences.apps().keys().customNames())
|
||||||
LauncherPreferences.internal().versionCode(2)
|
LauncherPreferences.internal().versionCode(2)
|
||||||
|
|
||||||
migratePreferencesFromVersion2()
|
migratePreferencesFromVersion2(context)
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package de.jrpie.android.launcher.preferences.legacy
|
package de.jrpie.android.launcher.preferences.legacy
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import de.jrpie.android.launcher.actions.Action
|
import de.jrpie.android.launcher.actions.Action
|
||||||
import de.jrpie.android.launcher.actions.Gesture
|
import de.jrpie.android.launcher.actions.Gesture
|
||||||
import de.jrpie.android.launcher.actions.LauncherAction
|
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
|
* Migrate preferences from version 2 (used until version 0.0.21) to the current format
|
||||||
* (see [PREFERENCE_VERSION])
|
* (see [PREFERENCE_VERSION])
|
||||||
*/
|
*/
|
||||||
fun migratePreferencesFromVersion2() {
|
fun migratePreferencesFromVersion2(context: Context) {
|
||||||
assert(LauncherPreferences.internal().versionCode() == 2)
|
assert(LauncherPreferences.internal().versionCode() == 2)
|
||||||
// previously there was no setting for this
|
// previously there was no setting for this
|
||||||
Action.setActionForGesture(Gesture.BACK, LauncherAction.CHOOSE)
|
Action.setActionForGesture(Gesture.BACK, LauncherAction.CHOOSE)
|
||||||
LauncherPreferences.internal().versionCode(3)
|
LauncherPreferences.internal().versionCode(3)
|
||||||
migratePreferencesFromVersion3()
|
migratePreferencesFromVersion3(context)
|
||||||
}
|
}
|
|
@ -1,17 +1,17 @@
|
||||||
package de.jrpie.android.launcher.preferences.legacy
|
package de.jrpie.android.launcher.preferences.legacy
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.SharedPreferences.Editor
|
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.AbstractAppInfo
|
||||||
|
import de.jrpie.android.launcher.apps.AppInfo
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
|
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
|
||||||
import de.jrpie.android.launcher.preferences.serialization.MapAbstractAppInfoStringPreferenceSerializer
|
import de.jrpie.android.launcher.preferences.serialization.MapAbstractAppInfoStringPreferenceSerializer
|
||||||
import de.jrpie.android.launcher.preferences.serialization.SetAbstractAppInfoPreferenceSerializer
|
import de.jrpie.android.launcher.preferences.serialization.SetAbstractAppInfoPreferenceSerializer
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
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
|
* 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() {
|
fun migratePreferencesFromVersion3(context: Context) {
|
||||||
assert(PREFERENCE_VERSION == 4)
|
|
||||||
assert(LauncherPreferences.internal().versionCode() == 3)
|
assert(LauncherPreferences.internal().versionCode() == 3)
|
||||||
|
|
||||||
val preferences = LauncherPreferences.getSharedPreferences()
|
val preferences = LauncherPreferences.getSharedPreferences()
|
||||||
|
@ -82,4 +81,5 @@ fun migratePreferencesFromVersion3() {
|
||||||
}
|
}
|
||||||
|
|
||||||
LauncherPreferences.internal().versionCode(4)
|
LauncherPreferences.internal().versionCode(4)
|
||||||
|
migratePreferencesFromVersion4(context)
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -3,10 +3,10 @@ package de.jrpie.android.launcher.preferences.legacy
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.core.content.edit
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
import de.jrpie.android.launcher.preferences.theme.Background
|
import de.jrpie.android.launcher.preferences.theme.Background
|
||||||
import de.jrpie.android.launcher.preferences.theme.ColorTheme
|
import de.jrpie.android.launcher.preferences.theme.ColorTheme
|
||||||
import androidx.core.content.edit
|
|
||||||
|
|
||||||
|
|
||||||
private fun migrateStringPreference(
|
private fun migrateStringPreference(
|
||||||
|
@ -392,5 +392,5 @@ fun migratePreferencesFromVersionUnknown(context: Context) {
|
||||||
LauncherPreferences.internal().versionCode(1)
|
LauncherPreferences.internal().versionCode(1)
|
||||||
Log.i(TAG, "migrated preferences to version 1.")
|
Log.i(TAG, "migrated preferences to version 1.")
|
||||||
|
|
||||||
migratePreferencesFromVersion1()
|
migratePreferencesFromVersion1(context)
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@ package de.jrpie.android.launcher.preferences.serialization
|
||||||
import de.jrpie.android.launcher.apps.AbstractAppInfo
|
import de.jrpie.android.launcher.apps.AbstractAppInfo
|
||||||
import de.jrpie.android.launcher.apps.PinnedShortcutInfo
|
import de.jrpie.android.launcher.apps.PinnedShortcutInfo
|
||||||
import de.jrpie.android.launcher.widgets.Widget
|
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.PreferenceSerializationException
|
||||||
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializer
|
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializer
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
@ -46,6 +47,22 @@ class SetWidgetSerializer :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
class SetWidgetPanelSerializer :
|
||||||
|
PreferenceSerializer<java.util.Set<WidgetPanel>?, java.util.Set<java.lang.String>?> {
|
||||||
|
@Throws(PreferenceSerializationException::class)
|
||||||
|
override fun serialize(value: java.util.Set<WidgetPanel>?): java.util.Set<java.lang.String>? {
|
||||||
|
return value?.map(WidgetPanel::serialize)
|
||||||
|
?.toHashSet() as? java.util.Set<java.lang.String>
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(PreferenceSerializationException::class)
|
||||||
|
override fun deserialize(value: java.util.Set<java.lang.String>?): java.util.Set<WidgetPanel>? {
|
||||||
|
return value?.map(java.lang.String::toString)?.map(WidgetPanel::deserialize)
|
||||||
|
?.toHashSet() as? java.util.Set<WidgetPanel>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
class SetPinnedShortcutInfoPreferenceSerializer :
|
class SetPinnedShortcutInfoPreferenceSerializer :
|
||||||
|
|
|
@ -207,7 +207,6 @@ class HomeActivity : UIObject, Activity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||||
android.util.Log.e("Launcher", "on touch")
|
|
||||||
touchGestureDetector?.onTouchEvent(event)
|
touchGestureDetector?.onTouchEvent(event)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import de.jrpie.android.launcher.R
|
||||||
import de.jrpie.android.launcher.actions.Action
|
import de.jrpie.android.launcher.actions.Action
|
||||||
import de.jrpie.android.launcher.actions.Gesture
|
import de.jrpie.android.launcher.actions.Gesture
|
||||||
import de.jrpie.android.launcher.actions.LauncherAction
|
import de.jrpie.android.launcher.actions.LauncherAction
|
||||||
|
import de.jrpie.android.launcher.actions.WidgetPanelAction
|
||||||
import de.jrpie.android.launcher.ui.list.ListActivity
|
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) :
|
class OtherRecyclerAdapter(val activity: Activity) :
|
||||||
RecyclerView.Adapter<OtherRecyclerAdapter.ViewHolder>() {
|
RecyclerView.Adapter<OtherRecyclerAdapter.ViewHolder>() {
|
||||||
|
|
||||||
private val othersList: Array<LauncherAction> =
|
private val othersList: Array<Action> =
|
||||||
LauncherAction.entries.filter { it.isAvailable(activity) }.toTypedArray()
|
LauncherAction.entries.filter { it.isAvailable(activity) }
|
||||||
|
.plus(WidgetPanelAction(-1))
|
||||||
|
.toTypedArray()
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
|
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
|
||||||
View.OnClickListener {
|
View.OnClickListener {
|
||||||
|
@ -36,10 +39,12 @@ class OtherRecyclerAdapter(val activity: Activity) :
|
||||||
val pos = bindingAdapterPosition
|
val pos = bindingAdapterPosition
|
||||||
val content = othersList[pos]
|
val content = othersList[pos]
|
||||||
|
|
||||||
activity.finish()
|
|
||||||
val gestureId = (activity as? ListActivity)?.forGesture ?: return
|
val gestureId = (activity as? ListActivity)?.forGesture ?: return
|
||||||
val gesture = Gesture.byId(gestureId) ?: return
|
val gesture = Gesture.byId(gestureId) ?: return
|
||||||
Action.setActionForGesture(gesture, content)
|
content.showConfigurationDialog(activity) { configuredAction ->
|
||||||
|
Action.setActionForGesture(gesture, configuredAction)
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -48,11 +53,11 @@ class OtherRecyclerAdapter(val activity: Activity) :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
|
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
|
||||||
val otherLabel = activity.getString(othersList[i].label)
|
val otherLabel = othersList[i].label(activity)
|
||||||
val icon = othersList[i].icon
|
val icon = othersList[i].getIcon(activity)
|
||||||
|
|
||||||
viewHolder.textView.text = otherLabel
|
viewHolder.textView.text = otherLabel
|
||||||
viewHolder.iconView.setImageResource(icon)
|
viewHolder.iconView.setImageDrawable(icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import de.jrpie.android.launcher.actions.openAppsList
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
import de.jrpie.android.launcher.preferences.theme.ColorTheme
|
import de.jrpie.android.launcher.preferences.theme.ColorTheme
|
||||||
import de.jrpie.android.launcher.setDefaultHomeScreen
|
import de.jrpie.android.launcher.setDefaultHomeScreen
|
||||||
|
import de.jrpie.android.launcher.ui.widgets.manage.ManageWidgetPanelsActivity
|
||||||
import de.jrpie.android.launcher.ui.widgets.manage.ManageWidgetsActivity
|
import de.jrpie.android.launcher.ui.widgets.manage.ManageWidgetsActivity
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,6 +91,14 @@ class SettingsFragmentLauncher : PreferenceFragmentCompat() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val manageWidgetPanels = findPreference<androidx.preference.Preference>(
|
||||||
|
LauncherPreferences.widgets().keys().customPanels()
|
||||||
|
)
|
||||||
|
manageWidgetPanels?.setOnPreferenceClickListener {
|
||||||
|
startActivity(Intent(requireActivity(), ManageWidgetPanelsActivity::class.java))
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
val hiddenApps = findPreference<androidx.preference.Preference>(
|
val hiddenApps = findPreference<androidx.preference.Preference>(
|
||||||
LauncherPreferences.apps().keys().hidden()
|
LauncherPreferences.apps().keys().hidden()
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import android.view.ViewGroup
|
||||||
import androidx.core.graphics.contains
|
import androidx.core.graphics.contains
|
||||||
import androidx.core.view.size
|
import androidx.core.view.size
|
||||||
import de.jrpie.android.launcher.widgets.Widget
|
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.WidgetPosition
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
|
@ -20,22 +21,29 @@ import kotlin.math.max
|
||||||
/**
|
/**
|
||||||
* This only works in an Activity, not AppCompatActivity
|
* 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<Int, View>()
|
var widgetViewById = HashMap<Int, View>()
|
||||||
|
|
||||||
open fun updateWidgets(activity: Activity, widgets: Set<Widget>?) {
|
open fun updateWidgets(activity: Activity, widgets: Collection<Widget>?) {
|
||||||
if (widgets == null) {
|
synchronized(widgetViewById) {
|
||||||
return
|
if (widgets == null) {
|
||||||
}
|
return
|
||||||
Log.i("WidgetContainer", "updating ${activity.localClassName}")
|
}
|
||||||
widgetViewById.clear()
|
Log.i("WidgetContainer", "updating ${activity.localClassName}")
|
||||||
(0..<size).forEach { removeViewAt(0) }
|
widgetViewById.forEach { removeView(it.value) }
|
||||||
widgets.forEach { widget ->
|
widgetViewById.clear()
|
||||||
|
widgets.filter { it.panelId == widgetPanelId }.forEach { widget ->
|
||||||
widget.createView(activity)?.let {
|
widget.createView(activity)?.let {
|
||||||
addView(it, WidgetContainerView.Companion.LayoutParams(widget.position))
|
addView(it, LayoutParams(widget.position))
|
||||||
widgetViewById.put(widget.id, it)
|
widgetViewById.put(widget.id, it)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +75,6 @@ open class WidgetContainerView(context: Context, attrs: AttributeSet? = null) :
|
||||||
(0..<size).map { getChildAt(it) }.forEach {
|
(0..<size).map { getChildAt(it) }.forEach {
|
||||||
val position = (it.layoutParams as LayoutParams).position.getAbsoluteRect(mWidth, mHeight)
|
val position = (it.layoutParams as LayoutParams).position.getAbsoluteRect(mWidth, mHeight)
|
||||||
it.measure(makeMeasureSpec(position.width(), MeasureSpec.EXACTLY), makeMeasureSpec(position.height(), MeasureSpec.EXACTLY))
|
it.measure(makeMeasureSpec(position.width(), MeasureSpec.EXACTLY), makeMeasureSpec(position.height(), MeasureSpec.EXACTLY))
|
||||||
Log.e("measure", "$position")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find rightmost and bottom-most child
|
// Find rightmost and bottom-most child
|
||||||
|
@ -134,4 +141,4 @@ open class WidgetContainerView(context: Context, attrs: AttributeSet? = null) :
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package de.jrpie.android.launcher.ui.widgets
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.os.Bundle
|
||||||
|
import de.jrpie.android.launcher.R
|
||||||
|
import de.jrpie.android.launcher.databinding.ActivityWidgetPanelBinding
|
||||||
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
|
import de.jrpie.android.launcher.ui.UIObject
|
||||||
|
import de.jrpie.android.launcher.ui.widgets.manage.EXTRA_PANEL_ID
|
||||||
|
import de.jrpie.android.launcher.widgets.WidgetPanel
|
||||||
|
|
||||||
|
class WidgetPanelActivity : Activity(), UIObject {
|
||||||
|
lateinit var binding: ActivityWidgetPanelBinding
|
||||||
|
var widgetPanelId: Int = WidgetPanel.Companion.HOME.id
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super<Activity>.onCreate(savedInstanceState)
|
||||||
|
super<UIObject>.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<Activity>.onStart()
|
||||||
|
super<UIObject>.onStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isHomeScreen(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<AppCompatActivity>.onCreate(savedInstanceState)
|
||||||
|
super<UIObject>.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<EditText>(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<EditText>(R.id.dialog_create_widget_panel_edit_text)
|
||||||
|
?.setText(
|
||||||
|
getString(
|
||||||
|
R.string.widget_panel_default_name,
|
||||||
|
WidgetPanel.allocateId()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super<AppCompatActivity>.onStart()
|
||||||
|
super<UIObject>.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() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package de.jrpie.android.launcher.ui.widgets.manage
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.appwidget.AppWidgetProviderInfo
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.Resources
|
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.UIObject
|
||||||
import de.jrpie.android.launcher.ui.widgets.WidgetContainerView
|
import de.jrpie.android.launcher.ui.widgets.WidgetContainerView
|
||||||
import de.jrpie.android.launcher.widgets.AppWidget
|
import de.jrpie.android.launcher.widgets.AppWidget
|
||||||
|
import de.jrpie.android.launcher.widgets.WidgetPanel
|
||||||
import de.jrpie.android.launcher.widgets.WidgetPosition
|
import de.jrpie.android.launcher.widgets.WidgetPosition
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
@ -27,9 +27,13 @@ import kotlin.math.min
|
||||||
const val REQUEST_CREATE_APPWIDGET = 1
|
const val REQUEST_CREATE_APPWIDGET = 1
|
||||||
const val REQUEST_PICK_APPWIDGET = 2
|
const val REQUEST_PICK_APPWIDGET = 2
|
||||||
|
|
||||||
|
const val EXTRA_PANEL_ID = "widgetPanelId"
|
||||||
|
|
||||||
// We can't use AppCompatActivity, since some AppWidgets don't work there.
|
// We can't use AppCompatActivity, since some AppWidgets don't work there.
|
||||||
class ManageWidgetsActivity : Activity(), UIObject {
|
class ManageWidgetsActivity : Activity(), UIObject {
|
||||||
|
|
||||||
|
var panelId: Int = WidgetPanel.HOME.id
|
||||||
|
|
||||||
private var sharedPreferencesListener =
|
private var sharedPreferencesListener =
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey ->
|
SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey ->
|
||||||
if (prefKey == LauncherPreferences.widgets().keys().widgets()) {
|
if (prefKey == LauncherPreferences.widgets().keys().widgets()) {
|
||||||
|
@ -44,6 +48,9 @@ class ManageWidgetsActivity : Activity(), UIObject {
|
||||||
super<Activity>.onCreate(savedInstanceState)
|
super<Activity>.onCreate(savedInstanceState)
|
||||||
super<UIObject>.onCreate()
|
super<UIObject>.onCreate()
|
||||||
setContentView(R.layout.activity_manage_widgets)
|
setContentView(R.layout.activity_manage_widgets)
|
||||||
|
|
||||||
|
panelId = intent.extras?.getInt(EXTRA_PANEL_ID, WidgetPanel.HOME.id) ?: WidgetPanel.HOME.id
|
||||||
|
|
||||||
findViewById<FloatingActionButton>(R.id.manage_widgets_button_add).setOnClickListener {
|
findViewById<FloatingActionButton>(R.id.manage_widgets_button_add).setOnClickListener {
|
||||||
selectWidget()
|
selectWidget()
|
||||||
}
|
}
|
||||||
|
@ -54,9 +61,10 @@ class ManageWidgetsActivity : Activity(), UIObject {
|
||||||
insets
|
insets
|
||||||
}
|
}
|
||||||
|
|
||||||
findViewById<WidgetContainerView>(R.id.manage_widgets_container).updateWidgets(this,
|
findViewById<WidgetContainerView>(R.id.manage_widgets_container).let {
|
||||||
(application as Application).widgets.value
|
it.widgetPanelId = panelId
|
||||||
)
|
it.updateWidgets(this, (application as Application).widgets.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
|
@ -101,6 +109,10 @@ class ManageWidgetsActivity : Activity(), UIObject {
|
||||||
AppWidgetManager.EXTRA_APPWIDGET_ID,
|
AppWidgetManager.EXTRA_APPWIDGET_ID,
|
||||||
appWidgetHost.allocateAppWidgetId()
|
appWidgetHost.allocateAppWidgetId()
|
||||||
)
|
)
|
||||||
|
it.putExtra(
|
||||||
|
EXTRA_PANEL_ID,
|
||||||
|
panelId
|
||||||
|
)
|
||||||
}, REQUEST_PICK_APPWIDGET
|
}, REQUEST_PICK_APPWIDGET
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -124,7 +136,7 @@ class ManageWidgetsActivity : Activity(), UIObject {
|
||||||
display.height
|
display.height
|
||||||
)
|
)
|
||||||
|
|
||||||
val widget = AppWidget(appWidgetId, provider, position)
|
val widget = AppWidget(appWidgetId, position, panelId, provider)
|
||||||
LauncherPreferences.widgets().widgets(
|
LauncherPreferences.widgets().widgets(
|
||||||
(LauncherPreferences.widgets().widgets() ?: HashSet()).also {
|
(LauncherPreferences.widgets().widgets() ?: HashSet()).also {
|
||||||
it.add(widget)
|
it.add(widget)
|
||||||
|
@ -135,7 +147,7 @@ class ManageWidgetsActivity : Activity(), UIObject {
|
||||||
private fun configureWidget(data: Intent) {
|
private fun configureWidget(data: Intent) {
|
||||||
val extras = data.extras
|
val extras = data.extras
|
||||||
val appWidgetId = extras!!.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
|
val appWidgetId = extras!!.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
|
||||||
val widget = AppWidget(appWidgetId)
|
val widget = AppWidget(appWidgetId, panelId = panelId)
|
||||||
if (widget.isConfigurable(this)) {
|
if (widget.isConfigurable(this)) {
|
||||||
widget.configure(this, REQUEST_CREATE_APPWIDGET)
|
widget.configure(this, REQUEST_CREATE_APPWIDGET)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import de.jrpie.android.launcher.widgets.ClockWidget
|
||||||
import de.jrpie.android.launcher.widgets.LauncherAppWidgetProvider
|
import de.jrpie.android.launcher.widgets.LauncherAppWidgetProvider
|
||||||
import de.jrpie.android.launcher.widgets.LauncherClockWidgetProvider
|
import de.jrpie.android.launcher.widgets.LauncherClockWidgetProvider
|
||||||
import de.jrpie.android.launcher.widgets.LauncherWidgetProvider
|
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.WidgetPosition
|
||||||
import de.jrpie.android.launcher.widgets.bindAppWidgetOrRequestPermission
|
import de.jrpie.android.launcher.widgets.bindAppWidgetOrRequestPermission
|
||||||
import de.jrpie.android.launcher.widgets.getAppWidgetHost
|
import de.jrpie.android.launcher.widgets.getAppWidgetHost
|
||||||
|
@ -38,6 +39,7 @@ private const val REQUEST_WIDGET_PERMISSION = 29
|
||||||
class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
||||||
lateinit var binding: ActivitySelectWidgetBinding
|
lateinit var binding: ActivitySelectWidgetBinding
|
||||||
var widgetId: Int = -1
|
var widgetId: Int = -1
|
||||||
|
var widgetPanelId: Int = WidgetPanel.HOME.id
|
||||||
|
|
||||||
private fun tryBindWidget(info: LauncherWidgetProvider) {
|
private fun tryBindWidget(info: LauncherWidgetProvider) {
|
||||||
when (info) {
|
when (info) {
|
||||||
|
@ -53,13 +55,14 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
||||||
RESULT_OK,
|
RESULT_OK,
|
||||||
Intent().also {
|
Intent().also {
|
||||||
it.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
|
it.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
|
||||||
|
it.putExtra(EXTRA_PANEL_ID, widgetPanelId)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is LauncherClockWidgetProvider -> {
|
is LauncherClockWidgetProvider -> {
|
||||||
updateWidget(ClockWidget(widgetId, WidgetPosition(0,4,12,3)))
|
updateWidget(ClockWidget(widgetId, WidgetPosition(0, 4, 12, 3), widgetPanelId))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +82,7 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
||||||
|
|
||||||
|
|
||||||
widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
|
widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
|
||||||
|
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
|
||||||
if (widgetId == -1) {
|
if (widgetId == -1) {
|
||||||
widgetId = getAppWidgetHost().allocateAppWidgetId()
|
widgetId = getAppWidgetHost().allocateAppWidgetId()
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import androidx.core.graphics.toRect
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import de.jrpie.android.launcher.ui.widgets.WidgetContainerView
|
import de.jrpie.android.launcher.ui.widgets.WidgetContainerView
|
||||||
import de.jrpie.android.launcher.widgets.Widget
|
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.WidgetPosition
|
||||||
import de.jrpie.android.launcher.widgets.updateWidget
|
import de.jrpie.android.launcher.widgets.updateWidget
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
@ -28,8 +29,9 @@ import kotlin.math.min
|
||||||
/**
|
/**
|
||||||
* A variant of the [WidgetContainerView] which allows to manage widgets.
|
* A variant of the [WidgetContainerView] which allows to manage widgets.
|
||||||
*/
|
*/
|
||||||
class WidgetManagerView(context: Context, attrs: AttributeSet? = null) :
|
class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSet? = null) :
|
||||||
WidgetContainerView(context, attrs) {
|
WidgetContainerView(widgetPanelId, context, attrs) {
|
||||||
|
constructor(context: Context, attrs: AttributeSet?) : this(WidgetPanel.HOME.id, context, attrs)
|
||||||
|
|
||||||
val TOUCH_SLOP: Int
|
val TOUCH_SLOP: Int
|
||||||
val TOUCH_SLOP_SQUARE: Int
|
val TOUCH_SLOP_SQUARE: Int
|
||||||
|
@ -155,13 +157,14 @@ class WidgetManagerView(context: Context, attrs: AttributeSet? = null) :
|
||||||
selectedWidgetOverlayView?.mode = null
|
selectedWidgetOverlayView?.mode = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateWidgets(activity: Activity, widgets: Set<Widget>?) {
|
override fun updateWidgets(activity: Activity, widgets: Collection<Widget>?) {
|
||||||
super.updateWidgets(activity, widgets)
|
super.updateWidgets(activity, widgets)
|
||||||
if (widgets == null) {
|
if (widgets == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
children.mapNotNull { it as? WidgetOverlayView }.forEach { removeView(it) }
|
||||||
|
|
||||||
widgets.forEach { widget ->
|
widgets.filter { it.panelId == widgetPanelId }.forEach { widget ->
|
||||||
WidgetOverlayView(activity).let {
|
WidgetOverlayView(activity).let {
|
||||||
addView(it)
|
addView(it)
|
||||||
it.widgetId = widget.id
|
it.widgetId = widget.id
|
||||||
|
|
|
@ -13,12 +13,13 @@ import de.jrpie.android.launcher.R
|
||||||
import de.jrpie.android.launcher.widgets.Widget
|
import de.jrpie.android.launcher.widgets.Widget
|
||||||
import de.jrpie.android.launcher.widgets.updateWidget
|
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_SIZE = 100
|
||||||
private const val HANDLE_EDGE_SIZE = (1.2 * HANDLE_SIZE).toInt()
|
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 {
|
class WidgetOverlayView : View {
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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<WidgetPanelsRecyclerAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
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<EditText>(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<EditText>(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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package de.jrpie.android.launcher.widgets;
|
package de.jrpie.android.launcher.widgets
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.appwidget.AppWidgetHostView
|
import android.appwidget.AppWidgetHostView
|
||||||
|
@ -11,7 +11,6 @@ import android.util.DisplayMetrics
|
||||||
import android.util.SizeF
|
import android.util.SizeF
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import de.jrpie.android.launcher.Application
|
import de.jrpie.android.launcher.Application
|
||||||
import de.jrpie.android.launcher.ui.HomeActivity
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@ -20,6 +19,7 @@ import kotlinx.serialization.Serializable
|
||||||
class AppWidget(
|
class AppWidget(
|
||||||
override val id: Int,
|
override val id: Int,
|
||||||
override var position: WidgetPosition = WidgetPosition(0,0,1,1),
|
override var position: WidgetPosition = WidgetPosition(0,0,1,1),
|
||||||
|
override var panelId: Int = WidgetPanel.HOME.id,
|
||||||
override var allowInteraction: Boolean = false,
|
override var allowInteraction: Boolean = false,
|
||||||
|
|
||||||
// We keep track of packageName, className and user to make it possible to restore the widget
|
// We keep track of packageName, className and user to make it possible to restore the widget
|
||||||
|
@ -31,10 +31,16 @@ class AppWidget(
|
||||||
): Widget() {
|
): Widget() {
|
||||||
|
|
||||||
|
|
||||||
constructor(id: Int, widgetProviderInfo: AppWidgetProviderInfo, position: WidgetPosition) :
|
constructor(
|
||||||
|
id: Int,
|
||||||
|
position: WidgetPosition,
|
||||||
|
panelId: Int,
|
||||||
|
widgetProviderInfo: AppWidgetProviderInfo
|
||||||
|
) :
|
||||||
this(
|
this(
|
||||||
id,
|
id,
|
||||||
position,
|
position,
|
||||||
|
panelId,
|
||||||
false,
|
false,
|
||||||
widgetProviderInfo.provider.packageName,
|
widgetProviderInfo.provider.packageName,
|
||||||
widgetProviderInfo.provider.className,
|
widgetProviderInfo.provider.className,
|
||||||
|
|
|
@ -14,6 +14,7 @@ import kotlinx.serialization.Serializable
|
||||||
class ClockWidget(
|
class ClockWidget(
|
||||||
override val id: Int,
|
override val id: Int,
|
||||||
override var position: WidgetPosition,
|
override var position: WidgetPosition,
|
||||||
|
override val panelId: Int,
|
||||||
override var allowInteraction: Boolean = true
|
override var allowInteraction: Boolean = true
|
||||||
) : Widget() {
|
) : Widget() {
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import kotlinx.serialization.json.Json
|
||||||
sealed class Widget {
|
sealed class Widget {
|
||||||
abstract val id: Int
|
abstract val id: Int
|
||||||
abstract var position: WidgetPosition
|
abstract var position: WidgetPosition
|
||||||
|
abstract val panelId: Int
|
||||||
abstract var allowInteraction: Boolean
|
abstract var allowInteraction: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,6 +37,10 @@ sealed class Widget {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getPanel(): WidgetPanel? {
|
||||||
|
return WidgetPanel.byId(panelId)
|
||||||
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,9 +74,19 @@ fun getAppWidgetProviders( context: Context ): List<LauncherWidgetProvider> {
|
||||||
|
|
||||||
|
|
||||||
fun updateWidget(widget: Widget) {
|
fun updateWidget(widget: Widget) {
|
||||||
var widgets = LauncherPreferences.widgets().widgets() ?: setOf()
|
LauncherPreferences.widgets().widgets(
|
||||||
widgets = widgets.minus(widget).plus(widget)
|
(LauncherPreferences.widgets().widgets() ?: setOf())
|
||||||
LauncherPreferences.widgets().widgets(widgets)
|
.minus(widget)
|
||||||
|
.plus(widget)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateWidgetPanel(widgetPanel: WidgetPanel) {
|
||||||
|
LauncherPreferences.widgets().customPanels(
|
||||||
|
(LauncherPreferences.widgets().customPanels() ?: setOf())
|
||||||
|
.minus(widgetPanel)
|
||||||
|
.plus(widgetPanel)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.getAppWidgetHost(): AppWidgetHost {
|
fun Context.getAppWidgetHost(): AppWidgetHost {
|
||||||
|
|
11
app/src/main/res/drawable/baseline_widgets_24.xml
Normal file
11
app/src/main/res/drawable/baseline_widgets_24.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:textColor"
|
||||||
|
android:pathData="M13,13v8h8v-8h-8zM3,21h8v-8L3,13v8zM3,3v8h8L11,3L3,3zM16.66,1.69L11,7.34 16.66,13l5.66,-5.66 -5.66,-5.65z" />
|
||||||
|
|
||||||
|
</vector>
|
73
app/src/main/res/layout/activity_manage_widget_panels.xml
Normal file
73
app/src/main/res/layout/activity_manage_widget_panels.xml
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
tools:context=".ui.widgets.manage.ManageWidgetPanelsActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/manage_widget_panels_appbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@null"
|
||||||
|
app:elevation="0dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manage_widget_panels_heading"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minHeight="?actionBarSize"
|
||||||
|
android:padding="@dimen/appbar_padding"
|
||||||
|
android:text="@string/widget_panels_title"
|
||||||
|
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
|
||||||
|
android:textSize="30sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/manage_widget_panels_close"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:includeFontPadding="true"
|
||||||
|
android:paddingLeft="16sp"
|
||||||
|
android:paddingRight="16sp"
|
||||||
|
android:src="@drawable/baseline_close_24"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/manage_widget_panels_recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/manage_widget_panels_appbar" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/manage_widget_panels_add_panel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:src="@drawable/baseline_add_24"
|
||||||
|
app:layout_anchor="@+id/manage_widget_panels_recycler"
|
||||||
|
app:layout_anchorGravity="end|bottom"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
15
app/src/main/res/layout/activity_widget_panel.xml
Normal file
15
app/src/main/res/layout/activity_widget_panel.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/home_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:longClickable="false"
|
||||||
|
tools:context=".ui.widgets.WidgetPanelActivity">
|
||||||
|
|
||||||
|
<de.jrpie.android.launcher.ui.widgets.WidgetContainerView
|
||||||
|
android:id="@+id/widget_panel_widget_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
18
app/src/main/res/layout/dialog_create_widget_panel.xml
Normal file
18
app/src/main/res/layout/dialog_create_widget_panel.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="@style/AlertDialogCustom"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/dialog_create_widget_panel_edit_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="text"
|
||||||
|
tools:ignore="LabelFor" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
18
app/src/main/res/layout/dialog_rename_widget_panel.xml
Normal file
18
app/src/main/res/layout/dialog_rename_widget_panel.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="@style/AlertDialogCustom"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/dialog_rename_widget_panel_edit_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="text"
|
||||||
|
tools:ignore="LabelFor" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
26
app/src/main/res/layout/dialog_select_widget_panel.xml
Normal file
26
app/src/main/res/layout/dialog_select_widget_panel.xml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="10dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_select_widget_panel_info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/dialog_select_widget_panel_info_no_panels"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/dialog_select_widget_panel_recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
23
app/src/main/res/layout/list_widget_panels_row.xml
Normal file
23
app/src/main/res/layout/list_widget_panels_row.xml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/list_widget_panels_row_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="15sp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/list_widget_panels_label"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="60sp"
|
||||||
|
android:layout_marginEnd="5dp"
|
||||||
|
android:gravity="start"
|
||||||
|
android:text=""
|
||||||
|
android:textSize="20sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -10,6 +10,7 @@
|
||||||
<string name="settings_internal_started_time_key" translatable="false">internal.first_startup</string>
|
<string name="settings_internal_started_time_key" translatable="false">internal.first_startup</string>
|
||||||
<string name="settings_internal_version_code_key" translatable="false">internal.version_code</string>
|
<string name="settings_internal_version_code_key" translatable="false">internal.version_code</string>
|
||||||
<string name="settings_widgets_widgets_key" translatable="false">widgets.widgets</string>
|
<string name="settings_widgets_widgets_key" translatable="false">widgets.widgets</string>
|
||||||
|
<string name="settings_widgets_custom_panels_key" translatable="false">widgets.custom_panels</string>
|
||||||
<string name="settings_apps_favorites_key" translatable="false">apps.favorites</string>
|
<string name="settings_apps_favorites_key" translatable="false">apps.favorites</string>
|
||||||
<string name="settings_apps_hidden_key" translatable="false">apps.hidden</string>
|
<string name="settings_apps_hidden_key" translatable="false">apps.hidden</string>
|
||||||
<string name="settings_apps_pinned_shortcuts_key" translatable="false">apps.pinned_shortcuts</string>
|
<string name="settings_apps_pinned_shortcuts_key" translatable="false">apps.pinned_shortcuts</string>
|
||||||
|
|
|
@ -99,6 +99,7 @@
|
||||||
<string name="settings_gesture_description_time">Click on time</string>
|
<string name="settings_gesture_description_time">Click on time</string>
|
||||||
|
|
||||||
<string name="settings_widgets_widgets">Manage widgets</string>
|
<string name="settings_widgets_widgets">Manage widgets</string>
|
||||||
|
<string name="settings_widgets_custom_panels">Manage widget panels</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="settings_apps_choose">Choose App</string>
|
<string name="settings_apps_choose">Choose App</string>
|
||||||
|
@ -400,5 +401,23 @@
|
||||||
|
|
||||||
<string name="widget_clock_label">Clock</string>
|
<string name="widget_clock_label">Clock</string>
|
||||||
<string name="widget_clock_description">The default clock of μLauncher</string>
|
<string name="widget_clock_description">The default clock of μLauncher</string>
|
||||||
|
<string name="manage_widget_panels_delete">Delete</string>
|
||||||
|
<string name="manage_widget_panels_rename">Rename</string>
|
||||||
|
|
||||||
|
<string name="widget_panel_default_name">Widget Panel #%1$d</string>
|
||||||
|
<plurals name="widget_panel_number_of_widgets">
|
||||||
|
<item quantity="one">Contains %d widget.</item>
|
||||||
|
<item quantity="other">Contains %d widgets.</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="dialog_ok">Ok</string>
|
||||||
|
<string name="widget_panels_title">Widget Panels</string>
|
||||||
|
<string name="dialog_select_widget_panel_title">Select a Widget Panel</string>
|
||||||
|
<string name="dialog_create_widget_panel_title">Create new widget panel</string>
|
||||||
|
<string name="dialog_select_widget_panel_info_no_panels"><![CDATA[No widget panels found. You can create widget panels in Settings > Launcher > Manage Widget Panels.]]></string>
|
||||||
|
<string name="list_other_open_widget_panel">Open Widget Panel</string>
|
||||||
|
<string name="alert_widget_panel_not_found">This widget panel no longer exists.</string>
|
||||||
|
<string name="settings_launcher_section_widgets">Widgets</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -3,14 +3,10 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
app:allowDividerAbove="false" > <!-- general -->
|
app:allowDividerAbove="false" > <!-- general -->
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="@string/settings_general_choose_home_screen_key"
|
android:key="@string/settings_general_choose_home_screen_key"
|
||||||
android:title="@string/settings_general_choose_home_screen"/>
|
android:title="@string/settings_general_choose_home_screen"/>
|
||||||
<Preference
|
|
||||||
android:key="@string/settings_widgets_widgets_key"
|
|
||||||
android:title="@string/settings_widgets_widgets"/>
|
|
||||||
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
app:allowDividerAbove="false"
|
app:allowDividerAbove="false"
|
||||||
|
@ -178,6 +174,16 @@
|
||||||
android:defaultValue="false"/>
|
android:defaultValue="false"/>
|
||||||
|
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
<PreferenceCategory
|
||||||
|
android:title="@string/settings_launcher_section_widgets"
|
||||||
|
app:allowDividerAbove="false">
|
||||||
|
<Preference
|
||||||
|
android:key="@string/settings_widgets_widgets_key"
|
||||||
|
android:title="@string/settings_widgets_widgets" />
|
||||||
|
<Preference
|
||||||
|
android:key="@string/settings_widgets_custom_panels_key"
|
||||||
|
android:title="@string/settings_widgets_custom_panels" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/settings_launcher_section_display"
|
android:title="@string/settings_launcher_section_display"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue