mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-27 15:26:58 +00:00
feat : initial implementation icon gesture support (#5266)
- closes : #2787 - closes : #5259
This commit is contained in:
@@ -99,6 +99,13 @@
|
||||
<!-- which overlay to use by default -->
|
||||
<string name="config_default_overlay" translatable="false">suck_in</string>
|
||||
|
||||
|
||||
<!-- swipe gesture key -->
|
||||
<string name="pref_key_swipe_up" translatable="false">pref_swipe_up</string>
|
||||
<string name="pref_key_swipe_down" translatable="false">pref_swipe_down</string>
|
||||
<string name="pref_key_swipe_right" translatable="false">pref_swipe_right</string>
|
||||
<string name="pref_key_swipe_left" translatable="false">pref_swipe_left</string>
|
||||
|
||||
<bool name="config_default_show_hotseat">true</bool>
|
||||
<bool name="config_default_always_reload_icons">true</bool>
|
||||
<bool name="config_default_dark_status_bar">false</bool>
|
||||
|
||||
@@ -466,6 +466,8 @@
|
||||
<string name="gesture_swipe_down">Swipe down</string>
|
||||
<string name="gesture_home_tap">Home button</string>
|
||||
<string name="gesture_back_tap">Back button</string>
|
||||
<string name="gesture_swipe_left">Swipe left</string>
|
||||
<string name="gesture_swipe_right">Swipe right</string>
|
||||
|
||||
<string name="gesture_handler_no_op">Do nothing</string>
|
||||
<string name="gesture_handler_sleep">Sleep</string>
|
||||
|
||||
@@ -2,6 +2,7 @@ package app.lawnchair.gestures
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.view.GestureDetector
|
||||
import android.view.GestureDetector.SimpleOnGestureListener
|
||||
import android.view.MotionEvent
|
||||
@@ -9,53 +10,64 @@ import android.view.View
|
||||
import android.view.View.OnTouchListener
|
||||
import kotlin.math.abs
|
||||
|
||||
open class DirectionalGestureListener(ctx: Context?) : OnTouchListener {
|
||||
private val mGestureDetector: GestureDetector
|
||||
abstract class DirectionalGestureListener(ctx: Context?) : OnTouchListener {
|
||||
private val mGestureDetector = GestureDetector(ctx, GestureListener())
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||
return mGestureDetector.onTouchEvent(event)
|
||||
}
|
||||
|
||||
private inner class GestureListener : SimpleOnGestureListener() {
|
||||
inner class GestureListener : SimpleOnGestureListener() {
|
||||
|
||||
private fun shouldReactToSwipe(diff: Float, velocity: Float): Boolean = abs(diff) > SWIPE_THRESHOLD && abs(velocity) > SWIPE_VELOCITY_THRESHOLD
|
||||
|
||||
override fun onDown(e: MotionEvent): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun shouldReactToSwipe(diff: Float, velocity: Float): Boolean = abs(diff) > SWIPE_THRESHOLD && abs(velocity) > SWIPE_VELOCITY_THRESHOLD
|
||||
override fun onFling(
|
||||
e1: MotionEvent?,
|
||||
e2: MotionEvent,
|
||||
velocityX: Float,
|
||||
velocityY: Float,
|
||||
): Boolean {
|
||||
val diffY = e2.y - (e1?.y ?: 0f)
|
||||
val diffX = e2.x - (e1?.x ?: 0f)
|
||||
|
||||
override fun onFling(e1: MotionEvent?, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
|
||||
return try {
|
||||
val diffY = e2.y - (e1?.y ?: 0f)
|
||||
val diffX = e2.x - (e1?.x ?: 0f)
|
||||
Log.d("GESTURE_DETECTION", "onFling: y " + shouldReactToSwipe(diffY, velocityY))
|
||||
Log.d("GESTURE_DETECTION", "onFling: X " + shouldReactToSwipe(diffX, velocityX))
|
||||
|
||||
when {
|
||||
abs(diffX) > abs(diffY) && shouldReactToSwipe(diffX, velocityX) -> {
|
||||
if (diffX > 0) onSwipeRight() else onSwipeLeft()
|
||||
true
|
||||
return when {
|
||||
shouldReactToSwipe(diffY, velocityY) -> {
|
||||
if (diffY < 0) {
|
||||
Log.d("GESTURE_DETECTION", "Swipe Up Detected")
|
||||
onSwipeTop()
|
||||
} else {
|
||||
Log.d("GESTURE_DETECTION", "Swipe Down Detected")
|
||||
onSwipeDown()
|
||||
}
|
||||
shouldReactToSwipe(diffY, velocityY) -> {
|
||||
if (diffY > 0) onSwipeBottom() else onSwipeTop()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
shouldReactToSwipe(diffX, velocityX) -> {
|
||||
if (diffX > 0) {
|
||||
Log.d("GESTURE_DETECTION", "Swipe Right Detected")
|
||||
onSwipeRight()
|
||||
} else {
|
||||
Log.d("GESTURE_DETECTION", "Swipe Left Detected")
|
||||
onSwipeLeft()
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onSwipeRight() {}
|
||||
fun onSwipeLeft() {}
|
||||
fun onSwipeTop() {}
|
||||
open fun onSwipeBottom() {}
|
||||
|
||||
init {
|
||||
mGestureDetector = GestureDetector(ctx, GestureListener())
|
||||
}
|
||||
abstract fun onSwipeRight()
|
||||
abstract fun onSwipeLeft()
|
||||
abstract fun onSwipeTop()
|
||||
abstract fun onSwipeDown()
|
||||
|
||||
companion object {
|
||||
private const val SWIPE_THRESHOLD = 100
|
||||
|
||||
42
lawnchair/src/app/lawnchair/gestures/IconGestureListener.kt
Normal file
42
lawnchair/src/app/lawnchair/gestures/IconGestureListener.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package app.lawnchair.gestures
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import app.lawnchair.gestures.config.GestureHandlerConfig
|
||||
import app.lawnchair.gestures.type.GestureType
|
||||
import app.lawnchair.launcher
|
||||
import app.lawnchair.preferences2.PreferenceManager2
|
||||
import com.android.launcher3.model.data.ItemInfo
|
||||
import com.android.launcher3.util.VibratorWrapper
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class IconGestureListener(
|
||||
private val context: Context,
|
||||
private val prefs: PreferenceManager2,
|
||||
private val cmp: ItemInfo?,
|
||||
) : DirectionalGestureListener(context) {
|
||||
|
||||
override fun onSwipeRight() = handleGesture(GestureType.SWIPE_RIGHT)
|
||||
override fun onSwipeLeft() = handleGesture(GestureType.SWIPE_LEFT)
|
||||
override fun onSwipeTop() = handleGesture(GestureType.SWIPE_UP)
|
||||
override fun onSwipeDown() = handleGesture(GestureType.SWIPE_DOWN)
|
||||
|
||||
private fun handleGesture(gestureType: GestureType) {
|
||||
Log.d("GESTURE_HANDLER", "Handling gesture: ${gestureType.name}")
|
||||
|
||||
cmp?.componentKey?.let {
|
||||
context.launcher.lifecycleScope.launch {
|
||||
val gesture = prefs.getGestureForApp(it, gestureType).firstOrNull()
|
||||
if (gesture !is GestureHandlerConfig.NoOp) {
|
||||
Log.d("GESTURE_HANDLER", "Triggering gesture: ${gestureType.name}")
|
||||
VibratorWrapper.INSTANCE.get(context.launcher).vibrate(VibratorWrapper.OVERVIEW_HAPTIC)
|
||||
gesture?.createHandler(context)?.onTrigger(context.launcher)
|
||||
} else {
|
||||
Log.d("GESTURE_HANDLER", "NoOp gesture, ignoring")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
lawnchair/src/app/lawnchair/gestures/type/GestureType.kt
Normal file
19
lawnchair/src/app/lawnchair/gestures/type/GestureType.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
package app.lawnchair.gestures.type
|
||||
|
||||
import android.annotation.StringRes
|
||||
import android.content.Context
|
||||
import com.android.launcher3.R
|
||||
|
||||
enum class GestureType(@StringRes val keyResId: Int, @StringRes val labelResId: Int) {
|
||||
SWIPE_UP(R.string.pref_key_swipe_up, R.string.gesture_swipe_up),
|
||||
SWIPE_DOWN(R.string.pref_key_swipe_down, R.string.gesture_swipe_down),
|
||||
SWIPE_LEFT(R.string.pref_key_swipe_left, R.string.gesture_swipe_left),
|
||||
SWIPE_RIGHT(R.string.pref_key_swipe_right, R.string.gesture_swipe_right),
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun fromKey(key: String, context: Context): GestureType? {
|
||||
return entries.find { context.getString(it.keyResId) == key }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,11 +29,14 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.lawnchair.gestures.type.GestureType
|
||||
import app.lawnchair.launcher
|
||||
import app.lawnchair.preferences.getAdapter
|
||||
import app.lawnchair.preferences.preferenceManager
|
||||
import app.lawnchair.preferences2.asState
|
||||
import app.lawnchair.preferences2.preferenceManager2
|
||||
import app.lawnchair.ui.preferences.PreferenceActivity
|
||||
import app.lawnchair.ui.preferences.components.AppGesturePreference
|
||||
import app.lawnchair.ui.preferences.components.controls.SwitchPreference
|
||||
import app.lawnchair.ui.preferences.components.layout.ClickableIcon
|
||||
import app.lawnchair.ui.preferences.components.layout.PreferenceGroup
|
||||
@@ -41,10 +44,10 @@ import app.lawnchair.ui.preferences.navigation.Routes
|
||||
import app.lawnchair.ui.util.addIfNotNull
|
||||
import app.lawnchair.util.navigationBarsOrDisplayCutoutPadding
|
||||
import com.android.launcher3.LauncherAppState
|
||||
import com.android.launcher3.LauncherState
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.util.ComponentKey
|
||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun CustomizeDialog(
|
||||
@@ -168,5 +171,20 @@ fun CustomizeAppDialog(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (context.launcher.stateManager.state != LauncherState.ALL_APPS) {
|
||||
PreferenceGroup(heading = stringResource(R.string.gestures_label)) {
|
||||
listOf(
|
||||
GestureType.SWIPE_LEFT,
|
||||
GestureType.SWIPE_RIGHT,
|
||||
).map { gestureType ->
|
||||
AppGesturePreference(
|
||||
componentKey,
|
||||
gestureType,
|
||||
stringResource(id = gestureType.labelResId),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,16 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.floatPreferencesKey
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringSetPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import app.lawnchair.data.Converters
|
||||
import app.lawnchair.font.FontCache
|
||||
import app.lawnchair.gestures.config.GestureHandlerConfig
|
||||
import app.lawnchair.gestures.type.GestureType
|
||||
import app.lawnchair.hotseat.HotseatMode
|
||||
import app.lawnchair.icons.CustomAdaptiveIconDrawable
|
||||
import app.lawnchair.icons.shape.IconShape
|
||||
@@ -51,6 +54,7 @@ import com.android.launcher3.InvariantDeviceProfile.INDEX_DEFAULT
|
||||
import com.android.launcher3.LauncherAppState
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.graphics.IconShape as L3IconShape
|
||||
import com.android.launcher3.util.ComponentKey
|
||||
import com.android.launcher3.util.DynamicResource
|
||||
import com.android.launcher3.util.MainThreadInitializedObject
|
||||
import com.android.launcher3.util.SafeCloseable
|
||||
@@ -58,9 +62,11 @@ import com.patrykmichalik.opto.core.PreferenceManager
|
||||
import com.patrykmichalik.opto.core.firstBlocking
|
||||
import com.patrykmichalik.opto.core.setBlocking
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
class PreferenceManager2 private constructor(private val context: Context) :
|
||||
@@ -700,6 +706,25 @@ class PreferenceManager2 private constructor(private val context: Context) :
|
||||
.launchIn(scope)
|
||||
}
|
||||
|
||||
suspend fun setGestureForApp(key: ComponentKey, gestureType: GestureType, gesture: GestureHandlerConfig) {
|
||||
val cmp = Converters().fromComponentKey(key)
|
||||
val key = stringPreferencesKey("$cmp:${gestureType.name}")
|
||||
preferencesDataStore.edit { prefs ->
|
||||
prefs[key] = kotlinxJson.encodeToString(gesture)
|
||||
}
|
||||
}
|
||||
|
||||
fun getGestureForApp(key: ComponentKey, gestureType: GestureType): Flow<GestureHandlerConfig> {
|
||||
val cmp = Converters().fromComponentKey(key)
|
||||
val key = stringPreferencesKey("$cmp:${gestureType.name}")
|
||||
return preferencesDataStore.data.map { prefs ->
|
||||
prefs[key]?.let {
|
||||
runCatching { kotlinxJson.decodeFromString<GestureHandlerConfig>(it) }
|
||||
.getOrDefault(GestureHandlerConfig.NoOp)
|
||||
} ?: GestureHandlerConfig.NoOp
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeIconShape(shape: IconShape) {
|
||||
CustomAdaptiveIconDrawable.sInitialized = true
|
||||
CustomAdaptiveIconDrawable.sMaskId = shape.getHashString()
|
||||
|
||||
@@ -2,26 +2,38 @@ package app.lawnchair.ui.preferences.components
|
||||
|
||||
import android.R as AndroidR
|
||||
import android.app.Activity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.lawnchair.gestures.config.GestureHandlerConfig
|
||||
import app.lawnchair.gestures.config.GestureHandlerOption
|
||||
import app.lawnchair.gestures.type.GestureType
|
||||
import app.lawnchair.preferences.PreferenceAdapter
|
||||
import app.lawnchair.preferences2.preferenceManager2
|
||||
import app.lawnchair.ui.ModalBottomSheetContent
|
||||
import app.lawnchair.ui.preferences.components.layout.PreferenceDivider
|
||||
import app.lawnchair.ui.preferences.components.layout.PreferenceTemplate
|
||||
import app.lawnchair.ui.util.LocalBottomSheetHandler
|
||||
import com.android.launcher3.util.ComponentKey
|
||||
import com.patrykmichalik.opto.core.firstBlocking
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -104,3 +116,70 @@ fun GestureHandlerPreference(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppGesturePreference(
|
||||
cmp: ComponentKey,
|
||||
gestureType: GestureType,
|
||||
label: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val prefs = preferenceManager2()
|
||||
|
||||
var isExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
val currentConfig by produceState<GestureHandlerConfig>(initialValue = GestureHandlerConfig.NoOp) {
|
||||
prefs.getGestureForApp(cmp, gestureType).collect { value = it }
|
||||
}
|
||||
|
||||
fun onSelect(option: GestureHandlerOption) {
|
||||
scope.launch {
|
||||
val config = option.buildConfig(context as Activity) ?: return@launch
|
||||
prefs.setGestureForApp(cmp, gestureType, config)
|
||||
isExpanded = false
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
PreferenceTemplate(
|
||||
title = { Text(text = label) },
|
||||
description = { Text(text = currentConfig.getLabel(context)) },
|
||||
modifier = Modifier
|
||||
.clickable { isExpanded = !isExpanded }
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
AnimatedVisibility(visible = isExpanded) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 100.dp, max = 300.dp),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
itemsIndexed(options) { index, option ->
|
||||
if (index > 0) {
|
||||
PreferenceDivider(startIndent = 40.dp)
|
||||
}
|
||||
val selected = currentConfig::class.java == option.configClass
|
||||
PreferenceTemplate(
|
||||
title = { Text(option.getLabel(context)) },
|
||||
modifier = Modifier.clickable {
|
||||
onSelect(option)
|
||||
},
|
||||
startWidget = {
|
||||
RadioButton(
|
||||
selected = selected,
|
||||
onClick = null,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ import java.util.Locale;
|
||||
|
||||
import app.lawnchair.LawnchairApp;
|
||||
import app.lawnchair.font.FontManager;
|
||||
import app.lawnchair.gestures.IconGestureListener;
|
||||
import app.lawnchair.preferences.PreferenceManager;
|
||||
import app.lawnchair.preferences2.PreferenceManager2;
|
||||
import app.lawnchair.util.LawnchairUtilsKt;
|
||||
@@ -227,6 +228,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
private boolean mEnableIconUpdateAnimation = false;
|
||||
|
||||
private final PreferenceManager2 pref2;
|
||||
private IconGestureListener mGestureListener;
|
||||
|
||||
public BubbleTextView(Context context) {
|
||||
this(context, null, 0);
|
||||
@@ -378,6 +380,9 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
@UiThread
|
||||
public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
|
||||
applyFromWorkspaceItem(info, /* animate = */ false, /* staggerIndex = */ 0);
|
||||
if (info != null) {
|
||||
mGestureListener = new IconGestureListener(mContext, pref2, info);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -542,6 +547,12 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
|
||||
if (mGestureListener != null) {
|
||||
mGestureListener.onTouch(this, event);
|
||||
resetIconScale(true);
|
||||
}
|
||||
|
||||
// ignore events if they happen in padding area
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN
|
||||
&& shouldIgnoreTouchDown(event.getX(), event.getY())) {
|
||||
@@ -846,14 +857,13 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
}
|
||||
|
||||
public boolean shouldTextBeVisible() {
|
||||
// Text should be visible everywhere but the hotseat.
|
||||
// Text should be visible everywhere, and in hotseat if getEnableLabelInDock is enabled.
|
||||
Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();
|
||||
ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null;
|
||||
if (info != null && (info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT
|
||||
&& info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION)) {
|
||||
return !PreferenceExtensionsKt.firstBlocking(pref2.getEnableLabelInDock());
|
||||
}
|
||||
return true;
|
||||
|
||||
return info == null || info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT
|
||||
&& info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
|
||||
|| PreferenceExtensionsKt.firstBlocking(pref2.getEnableLabelInDock());
|
||||
}
|
||||
|
||||
public void setTextVisibility(boolean visible) {
|
||||
@@ -1347,4 +1357,13 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns the ItemInfo of the app this icon represents. */
|
||||
public ItemInfo getItemInfo() {
|
||||
Object tag = getTag();
|
||||
if (tag instanceof ItemInfo itemInfo) {
|
||||
return itemInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ import app.lawnchair.LawnchairApp;
|
||||
import app.lawnchair.LawnchairAppKt;
|
||||
import app.lawnchair.preferences.PreferenceManager;
|
||||
import app.lawnchair.preferences2.PreferenceManager2;
|
||||
import app.lawnchair.smartspace.DoubleShadowTextView;
|
||||
import app.lawnchair.smartspace.SmartspaceAppWidgetProvider;
|
||||
import app.lawnchair.smartspace.model.LawnchairSmartspace;
|
||||
import app.lawnchair.smartspace.model.SmartspaceMode;
|
||||
@@ -1205,12 +1206,50 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
||||
*/
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
View touchedView = findViewAtPosition(ev.getX(), ev.getY());
|
||||
if (touchedView instanceof ShortcutAndWidgetContainer container) {
|
||||
container.onTouchEvent(ev);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isTrackpadMultiFingerSwipe(ev)) {
|
||||
return false;
|
||||
}
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
private View findViewAtPosition(float x, float y) {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof CellLayout) {
|
||||
CellLayout cellLayout = (CellLayout) child;
|
||||
View foundView = findViewInCellLayout(cellLayout, x - child.getLeft(), y - child.getTop());
|
||||
if (foundView != null) {
|
||||
return foundView;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private View findViewInCellLayout(CellLayout cellLayout, float x, float y) {
|
||||
final int count = cellLayout.getChildCount();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
View child = cellLayout.getChildAt(i);
|
||||
if (child.getVisibility() == VISIBLE && isPointInsideView(x, y, child)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isPointInsideView(float x, float y, View view) {
|
||||
return x >= view.getLeft() && x <= view.getRight() &&
|
||||
y >= view.getTop() && y <= view.getBottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed here because launcher has a fullscreen exclusion rect and doesn't
|
||||
* pilfer the pointers.
|
||||
|
||||
Reference in New Issue
Block a user