From 72f77c8294964a68ee1fedeb76a63a887ed0e651 Mon Sep 17 00:00:00 2001 From: Josia Pietsch Date: Thu, 24 Apr 2025 14:34:27 +0200 Subject: [PATCH] add option to enable / disable interaction with widgets --- .../de/jrpie/android/launcher/Application.kt | 5 ++ .../launcher/actions/LauncherAction.kt | 13 ++--- .../LauncherPreferences$Config.java | 4 +- .../launcher/preferences/Preferences.kt | 2 +- .../jrpie/android/launcher/ui/HomeActivity.kt | 26 ++++------ .../launcher/SettingsFragmentLauncher.kt | 9 ++++ .../ui/widgets/WidgetContainerView.kt | 50 +++++++++++++------ .../widgets/manage/ManageWidgetsActivity.kt | 45 +++++++++-------- .../ui/widgets/manage/SelectWidgetActivity.kt | 10 +--- .../ui/widgets/manage/WidgetManagerView.kt | 21 ++++---- .../ui/widgets/manage/WidgetOverlayView.kt | 29 +++++++---- .../android/launcher/widgets/AppWidget.kt | 30 +++++++---- .../android/launcher/widgets/ClockWidget.kt | 12 ++++- .../widgets/LauncherWidgetProvider.kt | 26 ++++++---- .../jrpie/android/launcher/widgets/Widget.kt | 17 ++++--- .../jrpie/android/launcher/widgets/Widgets.kt | 7 +-- .../main/res/drawable/baseline_clock_24.xml | 15 ++++++ app/src/main/res/values/donottranslate.xml | 2 +- app/src/main/res/values/strings.xml | 11 ++++ app/src/main/res/xml/preferences.xml | 4 ++ 20 files changed, 214 insertions(+), 124 deletions(-) create mode 100644 app/src/main/res/drawable/baseline_clock_24.xml diff --git a/app/src/main/java/de/jrpie/android/launcher/Application.kt b/app/src/main/java/de/jrpie/android/launcher/Application.kt index 3c2e3bc..775621c 100644 --- a/app/src/main/java/de/jrpie/android/launcher/Application.kt +++ b/app/src/main/java/de/jrpie/android/launcher/Application.kt @@ -22,6 +22,8 @@ import de.jrpie.android.launcher.apps.isPrivateSpaceLocked import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.migratePreferencesToNewVersion import de.jrpie.android.launcher.preferences.resetPreferences +import de.jrpie.android.launcher.widgets.LauncherWidgetProvider +import de.jrpie.android.launcher.widgets.Widget import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -32,6 +34,7 @@ const val APP_WIDGET_HOST_ID = 42; class Application : android.app.Application() { val apps = MutableLiveData>() + val widgets = MutableLiveData>() val privateSpaceLocked = MutableLiveData() lateinit var appWidgetHost: AppWidgetHost lateinit var appWidgetManager: AppWidgetManager @@ -98,6 +101,8 @@ class Application : android.app.Application() { customAppNames = LauncherPreferences.apps().customNames() } else if (pref == LauncherPreferences.apps().keys().pinnedShortcuts()) { loadApps() + } else if (pref == LauncherPreferences.widgets().keys().widgets()) { + widgets.postValue(LauncherPreferences.widgets().widgets() ?: setOf()) } } diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt index aa758a2..6ba467e 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt @@ -15,13 +15,12 @@ import de.jrpie.android.launcher.BuildConfig import de.jrpie.android.launcher.R import de.jrpie.android.launcher.actions.lock.LauncherAccessibilityService import de.jrpie.android.launcher.apps.AppFilter +import de.jrpie.android.launcher.apps.hidePrivateSpaceWhenLocked import de.jrpie.android.launcher.apps.isPrivateSpaceSupported import de.jrpie.android.launcher.apps.togglePrivateSpaceLock import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.ui.list.ListActivity import de.jrpie.android.launcher.ui.settings.SettingsActivity -import de.jrpie.android.launcher.ui.widgets.manage.ManageWidgetsActivity -import de.jrpie.android.launcher.ui.widgets.manage.SelectWidgetActivity import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -62,10 +61,7 @@ enum class LauncherAction( "choose_from_favorites", R.string.list_other_list_favorites, R.drawable.baseline_favorite_24, - { context -> - context.startActivity(Intent(context.applicationContext, ManageWidgetsActivity::class.java)) - }, - //openAppsList(context, favorite = true) }, + { context -> openAppsList(context, favorite = true) }, true ), CHOOSE_FROM_PRIVATE_SPACE( @@ -73,15 +69,12 @@ enum class LauncherAction( R.string.list_other_list_private_space, R.drawable.baseline_security_24, { context -> - context.startActivity(Intent(context.applicationContext, SelectWidgetActivity::class.java)) - }, - /*{ context -> if ((context.applicationContext as Application).privateSpaceLocked.value != true || !hidePrivateSpaceWhenLocked(context) ) { openAppsList(context, private = true) } - }, */ + }, available = { _ -> isPrivateSpaceSupported() } 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 5e51b52..575346a 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 @@ -27,7 +27,6 @@ import eu.jonahbauer.android.preference.annotations.Preferences; @Preference(name = "started_time", type = long.class), // see PREFERENCE_VERSION in de.jrpie.android.launcher.preferences.Preferences.kt @Preference(name = "version_code", type = int.class, defaultValue = "-1"), - @Preference(name = "widgets", type = Set.class, serializer = SetWidgetSerializer.class) }), @PreferenceGroup(name = "apps", prefix = "settings_apps_", suffix = "_key", value = { @Preference(name = "favorites", type = Set.class, serializer = SetAbstractAppInfoPreferenceSerializer.class), @@ -84,5 +83,8 @@ import eu.jonahbauer.android.preference.annotations.Preferences; @PreferenceGroup(name = "actions", prefix = "settings_actions_", suffix = "_key", value = { @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) + }), }) 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 dd0155b..332f4df 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 @@ -76,7 +76,7 @@ fun resetPreferences(context: Context) { LauncherPreferences.internal().versionCode(PREFERENCE_VERSION) deleteAllWidgets(context) - LauncherPreferences.internal().widgets( + LauncherPreferences.widgets().widgets( setOf( ClockWidget(-500, WidgetPosition(1,4,10,3)) ) 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 2900b0a..3f1e497 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 @@ -44,16 +44,14 @@ class HomeActivity : UIObject, Activity() { prefKey?.startsWith("display.") == true ) { recreate() - } - - if (prefKey?.startsWith("action.") == true) { + } else if (prefKey?.startsWith("action.") == true) { updateSettingsFallbackButtonVisibility() + } else if (prefKey == LauncherPreferences.widgets().keys().widgets()) { + binding.homeWidgetContainer.updateWidgets(this@HomeActivity, + LauncherPreferences.widgets().widgets() + ) } - if (prefKey?.startsWith("internal.widgets") == true) { - binding.homeWidgetContainer.updateWidgets(this) - - } } override fun onCreate(savedInstanceState: Bundle?) { @@ -77,7 +75,6 @@ class HomeActivity : UIObject, Activity() { binding.buttonFallbackSettings.setOnClickListener { LauncherAction.SETTINGS.invoke(this) } - binding.homeWidgetContainer.updateWidgets(this) } override fun onConfigurationChanged(newConfig: Configuration) { @@ -98,7 +95,6 @@ class HomeActivity : UIObject, Activity() { .registerOnSharedPreferenceChangeListener(sharedPreferencesListener) (application as Application).appWidgetHost.startListening() - binding.homeWidgetContainer.updateWidgets(this) } @@ -170,6 +166,10 @@ class HomeActivity : UIObject, Activity() { } } updateSettingsFallbackButtonVisibility() + + binding.homeWidgetContainer.updateWidgets(this@HomeActivity, + LauncherPreferences.widgets().widgets() + ) } override fun onDestroy() { @@ -206,12 +206,6 @@ class HomeActivity : UIObject, Activity() { return true } - override fun dispatchTouchEvent(event: MotionEvent): Boolean { - // TODO: fix! - touchGestureDetector?.onTouchEvent(event) - return false - } - override fun onTouchEvent(event: MotionEvent): Boolean { android.util.Log.e("Launcher", "on touch") touchGestureDetector?.onTouchEvent(event) @@ -225,4 +219,4 @@ class HomeActivity : UIObject, Activity() { override fun isHomeScreen(): Boolean { return true } -} \ No newline at end of file +} 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 a8efb43..8907f04 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.ManageWidgetsActivity /** @@ -81,6 +82,14 @@ class SettingsFragmentLauncher : PreferenceFragmentCompat() { true } + val manageWidgets = findPreference( + LauncherPreferences.widgets().keys().widgets() + ) + manageWidgets?.setOnPreferenceClickListener { + startActivity(Intent(requireActivity(), ManageWidgetsActivity::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 5371c63..04668ca 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 @@ -2,34 +2,60 @@ package de.jrpie.android.launcher.ui.widgets import android.app.Activity import android.content.Context -import android.os.Build -import android.os.Bundle +import android.graphics.PointF +import android.graphics.RectF import android.util.AttributeSet import android.util.Log -import android.util.SizeF +import android.view.MotionEvent +import android.view.View import android.view.View.MeasureSpec.makeMeasureSpec import android.view.ViewGroup +import androidx.core.graphics.contains import androidx.core.view.size -import de.jrpie.android.launcher.preferences.LauncherPreferences +import de.jrpie.android.launcher.widgets.Widget import de.jrpie.android.launcher.widgets.WidgetPosition import kotlin.math.max -// TODO: implement layout logic instead of linear layout /** * This only works in an Activity, not AppCompatActivity */ open class WidgetContainerView(context: Context, attrs: AttributeSet? = null) : ViewGroup(context, attrs) { - open fun updateWidgets(activity: Activity) { + var widgetViewById = HashMap() + + open fun updateWidgets(activity: Activity, widgets: Set?) { + if (widgets == null) { + return + } Log.i("WidgetContainer", "updating ${activity.localClassName}") + widgetViewById.clear() (0.. + widgets.forEach { widget -> widget.createView(activity)?.let { addView(it, WidgetContainerView.Companion.LayoutParams(widget.position)) + widgetViewById.put(widget.id, it) } } } + + override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { + if (ev == null) { + return false + } + val position = PointF(ev.x, ev.y) + + return widgetViewById.filter { + RectF( + it.value.x, + it.value.y, + it.value.x + it.value.width, + it.value.y + it.value.height + ).contains(position) == true + }.any { + Widget.byId(context, it.key)?.allowInteraction == false + } + } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { var maxHeight = suggestedMinimumHeight @@ -70,15 +96,11 @@ open class WidgetContainerView(context: Context, attrs: AttributeSet? = null) : override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { for (i in 0.. - if (prefKey?.startsWith("internal.widgets") == true) { - findViewById(R.id.manage_widgets_container).updateWidgets(this) + if (prefKey == LauncherPreferences.widgets().keys().widgets()) { + // We can't observe the livedata because this is not an AppCompatActivity + findViewById(R.id.manage_widgets_container).updateWidgets(this, + LauncherPreferences.widgets().widgets() + ) } } @@ -50,7 +54,9 @@ class ManageWidgetsActivity : Activity(), UIObject { insets } - findViewById(R.id.manage_widgets_container).updateWidgets(this) + findViewById(R.id.manage_widgets_container).updateWidgets(this, + (application as Application).widgets.value + ) } override fun onStart() { @@ -60,6 +66,14 @@ class ManageWidgetsActivity : Activity(), UIObject { LauncherPreferences.getSharedPreferences() .registerOnSharedPreferenceChangeListener(sharedPreferencesListener) + } + + override fun onResume() { + super.onResume() + findViewById(R.id.manage_widgets_container).updateWidgets(this, + LauncherPreferences.widgets().widgets() + ) + } override fun getTheme(): Resources.Theme { val mTheme = modifyTheme(super.getTheme()) @@ -111,30 +125,19 @@ class ManageWidgetsActivity : Activity(), UIObject { ) val widget = AppWidget(appWidgetId, provider, position) - LauncherPreferences.internal().widgets( - (LauncherPreferences.internal().widgets() ?: HashSet()).also { + LauncherPreferences.widgets().widgets( + (LauncherPreferences.widgets().widgets() ?: HashSet()).also { it.add(widget) } ) - - findViewById(R.id.manage_widgets_container).updateWidgets(this) } private fun configureWidget(data: Intent) { val extras = data.extras val appWidgetId = extras!!.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) - val appWidgetHost = (application as Application).appWidgetHost - val appWidgetInfo: AppWidgetProviderInfo = - (application as Application).appWidgetManager.getAppWidgetInfo(appWidgetId) ?: return - if (appWidgetInfo.configure != null) { - appWidgetHost.startAppWidgetConfigureActivityForResult( - this, - appWidgetId, - 0, - REQUEST_CREATE_APPWIDGET, - null - ) - + val widget = AppWidget(appWidgetId) + if (widget.isConfigurable(this)) { + widget.configure(this, REQUEST_CREATE_APPWIDGET) } else { createWidget(data) } @@ -146,14 +149,12 @@ class ManageWidgetsActivity : Activity(), UIObject { ) { super.onActivityResult(requestCode, resultCode, data) if (resultCode == RESULT_OK) { - Log.i("Manage Widgets", "result ok") if (requestCode == REQUEST_PICK_APPWIDGET) { configureWidget(data!!) } else if (requestCode == REQUEST_CREATE_APPWIDGET) { createWidget(data!!) } } else if (resultCode == RESULT_CANCELED && data != null) { - Log.i("Manage Widgets", "result canceled") val appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) if (appWidgetId != -1) { @@ -169,4 +170,4 @@ class ManageWidgetsActivity : Activity(), UIObject { 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/SelectWidgetActivity.kt b/app/src/main/java/de/jrpie/android/launcher/ui/widgets/manage/SelectWidgetActivity.kt index 0a2bee8..c414db6 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 @@ -6,28 +6,21 @@ import android.content.Intent import android.content.res.Resources import android.os.Build import android.os.Bundle -import android.util.DisplayMetrics -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView -import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import de.jrpie.android.launcher.R import de.jrpie.android.launcher.databinding.ActivitySelectWidgetBinding -import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.ui.UIObject 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.Widget import de.jrpie.android.launcher.widgets.WidgetPosition import de.jrpie.android.launcher.widgets.bindAppWidgetOrRequestPermission import de.jrpie.android.launcher.widgets.getAppWidgetHost @@ -110,7 +103,6 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject { if (requestCode == REQUEST_WIDGET_PERMISSION && resultCode == RESULT_OK) { data ?: return - Log.i("SelectWidget", "permission granted") val provider = (data.getSerializableExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER) as? AppWidgetProviderInfo) ?: return tryBindWidget(LauncherAppWidgetProvider(provider)) } @@ -139,7 +131,7 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject { } override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) { - val label = widgets[i].label + val label = widgets[i].loadLabel(this@SelectWidgetActivity) val description = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { widgets[i].loadDescription(this@SelectWidgetActivity) } else { 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 3ed86b0..2d41e13 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 @@ -1,7 +1,7 @@ package de.jrpie.android.launcher.ui.widgets.manage +import android.annotation.SuppressLint import android.app.Activity -import android.appwidget.AppWidgetHostView import android.content.Context import android.graphics.Point import android.graphics.Rect @@ -18,7 +18,6 @@ import androidx.core.graphics.contains import androidx.core.graphics.minus import androidx.core.graphics.toRect import androidx.core.view.children -import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.ui.widgets.WidgetContainerView import de.jrpie.android.launcher.widgets.Widget import de.jrpie.android.launcher.widgets.WidgetPosition @@ -72,11 +71,12 @@ class WidgetManagerView(context: Context, attrs: AttributeSet? = null) : private val longPressHandler = Handler(Looper.getMainLooper()) - override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { - onTouchEvent(ev) + + override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { return true } + @SuppressLint("ClickableViewAccessibility") override fun onTouchEvent(event: MotionEvent?): Boolean { if (event == null) { return false @@ -91,7 +91,7 @@ class WidgetManagerView(context: Context, attrs: AttributeSet? = null) : val position = (view.layoutParams as Companion.LayoutParams).position.getAbsoluteRect(width, height) selectedWidgetOverlayView = view - selectedWidgetView = Widget.byId(view.widgetId)?.findView(children) ?: return true + selectedWidgetView = widgetViewById.get(view.widgetId) ?: return true startWidgetPosition = position val positionInView = start.minus(Point(position.left, position.top)) @@ -136,7 +136,7 @@ class WidgetManagerView(context: Context, attrs: AttributeSet? = null) : if (event.actionMasked == MotionEvent.ACTION_UP) { longPressHandler.removeCallbacksAndMessages(null) val id = selectedWidgetOverlayView?.widgetId ?: return true - val widget = Widget.byId(id) ?: return true + val widget = Widget.byId(context, id) ?: return true widget.position = newPosition endInteraction() updateWidget(widget) @@ -155,10 +155,13 @@ class WidgetManagerView(context: Context, attrs: AttributeSet? = null) : selectedWidgetOverlayView?.mode = null } - override fun updateWidgets(activity: Activity) { - super.updateWidgets(activity) + override fun updateWidgets(activity: Activity, widgets: Set?) { + super.updateWidgets(activity, widgets) + if (widgets == null) { + return + } - LauncherPreferences.internal().widgets()?.forEach { widget -> + widgets.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 1b5eddb..0ce789f 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 @@ -9,7 +9,9 @@ import android.util.AttributeSet import android.view.View import android.widget.PopupMenu import androidx.core.graphics.toRectF +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. @@ -29,11 +31,9 @@ class WidgetOverlayView : View { handlePaint.style = Paint.Style.STROKE handlePaint.setARGB(255, 255, 255, 255) - selectedHandlePaint.style = Paint.Style.FILL_AND_STROKE selectedHandlePaint.setARGB(100, 255, 255, 255) - paint.style = Paint.Style.STROKE paint.setARGB(255, 255, 255, 255) } @@ -42,7 +42,7 @@ class WidgetOverlayView : View { var widgetId: Int = -1 set(newId) { field = newId - preview = Widget.byId(widgetId)?.getPreview(context) + preview = Widget.byId(context, widgetId)?.getPreview(context) } constructor(context: Context) : super(context) { @@ -74,7 +74,7 @@ class WidgetOverlayView : View { } } val bounds = getBounds() - canvas.drawRect(bounds, paint) + canvas.drawRoundRect(bounds.toRectF(), 5f, 5f, paint) if (mode == null) { return @@ -87,16 +87,26 @@ class WidgetOverlayView : View { } fun showPopupMenu() { + val widget = Widget.byId(context, widgetId)?: return val menu = PopupMenu(context, this) menu.menu.let { - it.add("Remove").setOnMenuItemClickListener { _ -> - Widget.byId(widgetId)?.delete(context) + it.add( + context.getString(R.string.widget_menu_remove) + ).setOnMenuItemClickListener { _ -> + Widget.byId(context, widgetId)?.delete(context) return@setOnMenuItemClickListener true } - it.add("Allow Interaction").setOnMenuItemClickListener { _ -> - return@setOnMenuItemClickListener true + it.add( + if (widget.allowInteraction) { + context.getString(R.string.widget_menu_disable_interaction) + } else { + context.getString(R.string.widget_menu_enable_interaction) } - it.add("Add Padding") + ).setOnMenuItemClickListener { _ -> + widget.allowInteraction = !widget.allowInteraction + updateWidget(widget) + return@setOnMenuItemClickListener true + } } menu.show() } @@ -118,5 +128,4 @@ class WidgetOverlayView : View { private fun getBounds(): Rect { return Rect(0,0, width, height) } - } \ 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 fc816cc..3e9a2eb 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,32 +1,26 @@ package de.jrpie.android.launcher.widgets; import android.app.Activity -import android.appwidget.AppWidgetHost import android.appwidget.AppWidgetHostView -import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProviderInfo import android.content.Context -import android.content.Intent import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle -import android.util.AttributeSet import android.util.DisplayMetrics -import android.util.Log import android.util.SizeF import android.view.View import de.jrpie.android.launcher.Application -import de.jrpie.android.launcher.preferences.LauncherPreferences +import de.jrpie.android.launcher.ui.HomeActivity import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json @Serializable @SerialName("widget:app") class AppWidget( override val id: Int, override var position: WidgetPosition = WidgetPosition(0,0,1,1), + override var allowInteraction: Boolean = false, // We keep track of packageName, className and user to make it possible to restore the widget // on a new device when restoring settings (currently not implemented) @@ -39,7 +33,9 @@ class AppWidget( constructor(id: Int, widgetProviderInfo: AppWidgetProviderInfo, position: WidgetPosition) : this( - id, position, + id, + position, + false, widgetProviderInfo.provider.packageName, widgetProviderInfo.provider.className, widgetProviderInfo.profile.hashCode() @@ -105,4 +101,20 @@ class AppWidget( override fun getPreview(context: Context): Drawable? { return context.getAppWidgetManager().getAppWidgetInfo(id)?.loadPreviewImage(context, DisplayMetrics.DENSITY_HIGH) } + + override fun isConfigurable(context: Context): Boolean { + return context.getAppWidgetManager().getAppWidgetInfo(id)?.configure != null + } + override fun configure(activity: Activity, requestCode: Int) { + if (!isConfigurable(activity)) { + return + } + activity.getAppWidgetHost().startAppWidgetConfigureActivityForResult( + activity, + id, + 0, + requestCode, + null + ) + } } 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 7accfe4..d819538 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 @@ -11,7 +11,11 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("widget:clock") -class ClockWidget(override val id: Int, override var position: WidgetPosition): Widget() { +class ClockWidget( + override val id: Int, + override var position: WidgetPosition, + override var allowInteraction: Boolean = true +) : Widget() { override fun createView(activity: Activity): View? { return ClockView(activity, null, id) @@ -28,4 +32,10 @@ class ClockWidget(override val id: Int, override var position: WidgetPosition): override fun getIcon(context: Context): Drawable? { return null } + + override fun isConfigurable(context: Context): Boolean { + return false + } + + override fun configure(activity: Activity, requestCode: Int) { } } \ No newline at end of file diff --git a/app/src/main/java/de/jrpie/android/launcher/widgets/LauncherWidgetProvider.kt b/app/src/main/java/de/jrpie/android/launcher/widgets/LauncherWidgetProvider.kt index 2755fe8..018b29b 100644 --- a/app/src/main/java/de/jrpie/android/launcher/widgets/LauncherWidgetProvider.kt +++ b/app/src/main/java/de/jrpie/android/launcher/widgets/LauncherWidgetProvider.kt @@ -5,17 +5,21 @@ import android.content.Context import android.graphics.drawable.Drawable import android.os.Build import android.util.DisplayMetrics +import androidx.appcompat.content.res.AppCompatResources +import de.jrpie.android.launcher.R sealed class LauncherWidgetProvider { - abstract val label: String? - + abstract fun loadLabel(context: Context): CharSequence? abstract fun loadPreviewImage(context: Context): Drawable? abstract fun loadIcon(context: Context): Drawable? abstract fun loadDescription(context: Context): CharSequence? } class LauncherAppWidgetProvider(val info: AppWidgetProviderInfo) : LauncherWidgetProvider() { - override val label: String? = info.label + + override fun loadLabel(context: Context): CharSequence? { + return info.loadLabel(context.packageManager) + } override fun loadPreviewImage(context: Context): Drawable? { return info.loadPreviewImage(context, DisplayMetrics.DENSITY_DEFAULT) } @@ -34,19 +38,21 @@ class LauncherAppWidgetProvider(val info: AppWidgetProviderInfo) : LauncherWidge } class LauncherClockWidgetProvider : LauncherWidgetProvider() { - override val label: String? - get() = "Clock" + + override fun loadLabel(context: Context): CharSequence? { + return context.getString(R.string.widget_clock_label) + } + + override fun loadDescription(context: Context): CharSequence? { + return context.getString(R.string.widget_clock_description) + } override fun loadPreviewImage(context: Context): Drawable? { return null } override fun loadIcon(context: Context): Drawable? { - return null - } - - override fun loadDescription(context: Context): CharSequence? { - return null + return AppCompatResources.getDrawable(context, R.drawable.baseline_clock_24) } } 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 e177baf..d3610dd 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 @@ -4,6 +4,7 @@ import android.app.Activity import android.content.Context import android.graphics.drawable.Drawable import android.view.View +import de.jrpie.android.launcher.Application import de.jrpie.android.launcher.preferences.LauncherPreferences import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @@ -13,6 +14,7 @@ import kotlinx.serialization.json.Json sealed class Widget { abstract val id: Int abstract var position: WidgetPosition + abstract var allowInteraction: Boolean /** * @param activity The activity where the view will be used. Must not be an AppCompatActivity. @@ -21,12 +23,14 @@ sealed class Widget { abstract fun findView(views: Sequence): View? abstract fun getPreview(context: Context): Drawable? abstract fun getIcon(context: Context): Drawable? + abstract fun isConfigurable(context: Context): Boolean + abstract fun configure(activity: Activity, requestCode: Int) fun delete(context: Context) { context.getAppWidgetHost().deleteAppWidgetId(id) - LauncherPreferences.internal().widgets( - LauncherPreferences.internal().widgets()?.also { + LauncherPreferences.widgets().widgets( + LauncherPreferences.widgets().widgets()?.also { it.remove(this) } ) @@ -47,9 +51,10 @@ sealed class Widget { fun deserialize(serialized: String): Widget { return Json.decodeFromString(serialized) } - fun byId(id: Int): Widget? { - return (LauncherPreferences.internal().widgets() ?: setOf()) - .firstOrNull { it.id == id } + fun byId(context: Context, id: Int): Widget? { + return (context.applicationContext as Application).widgets.value?.firstOrNull { + it.id == id + } } } -} \ 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 485045f..cd4ef29 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 @@ -3,17 +3,14 @@ package de.jrpie.android.launcher.widgets import android.app.Activity import android.app.Service import android.appwidget.AppWidgetHost -import android.appwidget.AppWidgetHostView import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProviderInfo import android.content.Context import android.content.Intent import android.content.pm.LauncherApps import android.os.Build -import android.os.Bundle import android.os.UserManager import android.util.Log -import android.util.SizeF import de.jrpie.android.launcher.Application import de.jrpie.android.launcher.preferences.LauncherPreferences @@ -77,9 +74,9 @@ fun getAppWidgetProviders( context: Context ): List { fun updateWidget(widget: Widget) { - var widgets = LauncherPreferences.internal().widgets() ?: setOf() + var widgets = LauncherPreferences.widgets().widgets() ?: setOf() widgets = widgets.minus(widget).plus(widget) - LauncherPreferences.internal().widgets(widgets) + LauncherPreferences.widgets().widgets(widgets) } fun Context.getAppWidgetHost(): AppWidgetHost { diff --git a/app/src/main/res/drawable/baseline_clock_24.xml b/app/src/main/res/drawable/baseline_clock_24.xml new file mode 100644 index 0000000..7968998 --- /dev/null +++ b/app/src/main/res/drawable/baseline_clock_24.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 9a99167..30e4cda 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -9,7 +9,7 @@ internal.started_before internal.first_startup internal.version_code - internal.widgets + widgets.widgets 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 e225558..a8813f8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -98,6 +98,8 @@ Time Click on time + Manage widgets + Choose App @@ -390,4 +392,13 @@ Can\'t open URL: no browser found. Choose Widget + Remove + Configure + Enable Interaction + Disable Interaction + + + Clock + The default clock of μLauncher + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 7d906ff..b4bc5f0 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -6,6 +6,10 @@ + +