mirror of
https://github.com/jrpie/Launcher.git
synced 2025-04-26 22:00:56 +02:00
add option to enable / disable interaction with widgets
This commit is contained in:
parent
13c88122b2
commit
72f77c8294
20 changed files with 214 additions and 124 deletions
|
@ -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<List<AbstractDetailedAppInfo>>()
|
||||
val widgets = MutableLiveData<Set<Widget>>()
|
||||
val privateSpaceLocked = MutableLiveData<Boolean>()
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 {}
|
|
@ -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))
|
||||
)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<androidx.preference.Preference>(
|
||||
LauncherPreferences.widgets().keys().widgets()
|
||||
)
|
||||
manageWidgets?.setOnPreferenceClickListener {
|
||||
startActivity(Intent(requireActivity(), ManageWidgetsActivity::class.java))
|
||||
true
|
||||
}
|
||||
|
||||
val hiddenApps = findPreference<androidx.preference.Preference>(
|
||||
LauncherPreferences.apps().keys().hidden()
|
||||
)
|
||||
|
|
|
@ -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<Int, View>()
|
||||
|
||||
open fun updateWidgets(activity: Activity, widgets: Set<Widget>?) {
|
||||
if (widgets == null) {
|
||||
return
|
||||
}
|
||||
Log.i("WidgetContainer", "updating ${activity.localClassName}")
|
||||
widgetViewById.clear()
|
||||
(0..<size).forEach { removeViewAt(0) }
|
||||
LauncherPreferences.internal().widgets()?.forEach { widget ->
|
||||
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..<size) {
|
||||
val child = getChildAt(i)
|
||||
//if (child.visibility != GONE) {
|
||||
val lp = child.layoutParams as LayoutParams
|
||||
|
||||
val position = lp.position.getAbsoluteRect(r - l, b - t)
|
||||
Log.e("onLayout", "$l, $t, $r, $b, absolute rect: $position")
|
||||
child.layout(position.left, position.top, position.right, position.bottom)
|
||||
val lp = child.layoutParams as LayoutParams
|
||||
val position = lp.position.getAbsoluteRect(r - l, b - t)
|
||||
child.layout(position.left, position.top, position.right, position.bottom)
|
||||
child.layoutParams.width = position.width()
|
||||
child.layoutParams.height = position.height()
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,12 +27,16 @@ import kotlin.math.min
|
|||
const val REQUEST_CREATE_APPWIDGET = 1
|
||||
const val REQUEST_PICK_APPWIDGET = 2
|
||||
|
||||
// We can't use AppCompatActivity, since some AppWidgets don't work there.
|
||||
class ManageWidgetsActivity : Activity(), UIObject {
|
||||
|
||||
private var sharedPreferencesListener =
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey ->
|
||||
if (prefKey?.startsWith("internal.widgets") == true) {
|
||||
findViewById<WidgetContainerView>(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<WidgetContainerView>(R.id.manage_widgets_container).updateWidgets(this,
|
||||
LauncherPreferences.widgets().widgets()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +54,9 @@ class ManageWidgetsActivity : Activity(), UIObject {
|
|||
insets
|
||||
}
|
||||
|
||||
findViewById<WidgetContainerView>(R.id.manage_widgets_container).updateWidgets(this)
|
||||
findViewById<WidgetContainerView>(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<WidgetContainerView>(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<WidgetContainerView>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<Widget>?) {
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) { }
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>): 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<LauncherWidgetProvider> {
|
|||
|
||||
|
||||
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 {
|
||||
|
|
15
app/src/main/res/drawable/baseline_clock_24.xml
Normal file
15
app/src/main/res/drawable/baseline_clock_24.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<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="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" />
|
||||
|
||||
<path
|
||||
android:fillColor="?android:textColor"
|
||||
android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z" />
|
||||
|
||||
</vector>
|
|
@ -9,7 +9,7 @@
|
|||
<string name="settings_internal_started_key" translatable="false">internal.started_before</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_widgets_key" translatable="false">internal.widgets</string>
|
||||
<string name="settings_widgets_widgets_key" translatable="false">widgets.widgets</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_pinned_shortcuts_key" translatable="false">apps.pinned_shortcuts</string>
|
||||
|
|
|
@ -98,6 +98,8 @@
|
|||
<string name="settings_gesture_time">Time</string>
|
||||
<string name="settings_gesture_description_time">Click on time</string>
|
||||
|
||||
<string name="settings_widgets_widgets">Manage widgets</string>
|
||||
|
||||
|
||||
<string name="settings_apps_choose">Choose App</string>
|
||||
|
||||
|
@ -390,4 +392,13 @@
|
|||
<string name="toast_activity_not_found_browser">Can\'t open URL: no browser found.</string>
|
||||
<string name="select_widget_title">Choose Widget</string>
|
||||
|
||||
<string name="widget_menu_remove">Remove</string>
|
||||
<string name="widget_menu_configure">Configure</string>
|
||||
<string name="widget_menu_enable_interaction">Enable Interaction</string>
|
||||
<string name="widget_menu_disable_interaction">Disable Interaction</string>
|
||||
|
||||
|
||||
<string name="widget_clock_label">Clock</string>
|
||||
<string name="widget_clock_description">The default clock of μLauncher</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
<Preference
|
||||
android:key="@string/settings_general_choose_home_screen_key"
|
||||
android:title="@string/settings_general_choose_home_screen"/>
|
||||
<Preference
|
||||
android:key="@string/settings_widgets_widgets_key"
|
||||
android:title="@string/settings_widgets_widgets"/>
|
||||
|
||||
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue