1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 @file:OptIn(ExperimentalFoundationApi::class)
18
19 package com.android.systemui.keyguard.ui.composable
20
21 import androidx.compose.foundation.ExperimentalFoundationApi
22 import androidx.compose.foundation.combinedClickable
23 import androidx.compose.foundation.gestures.awaitEachGesture
24 import androidx.compose.foundation.gestures.awaitFirstDown
25 import androidx.compose.foundation.interaction.MutableInteractionSource
26 import androidx.compose.foundation.layout.Box
27 import androidx.compose.foundation.layout.BoxScope
28 import androidx.compose.runtime.Composable
29 import androidx.compose.runtime.getValue
30 import androidx.compose.runtime.mutableStateOf
31 import androidx.compose.runtime.remember
32 import androidx.compose.ui.Modifier
33 import androidx.compose.ui.geometry.Rect
34 import androidx.compose.ui.input.pointer.pointerInput
35 import androidx.lifecycle.compose.collectAsStateWithLifecycle
36 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
37
38 /** Container for lockscreen content that handles long-press to bring up the settings menu. */
39 @Composable
LockscreenLongPressnull40 fun LockscreenLongPress(
41 viewModel: KeyguardLongPressViewModel,
42 modifier: Modifier = Modifier,
43 content: @Composable BoxScope.(onSettingsMenuPlaces: (coordinates: Rect?) -> Unit) -> Unit,
44 ) {
45 val isEnabled: Boolean by
46 viewModel.isLongPressHandlingEnabled.collectAsStateWithLifecycle(initialValue = false)
47 val (settingsMenuBounds, setSettingsMenuBounds) = remember { mutableStateOf<Rect?>(null) }
48 val interactionSource = remember { MutableInteractionSource() }
49
50 Box(
51 modifier =
52 modifier
53 .combinedClickable(
54 enabled = isEnabled,
55 onLongClick = viewModel::onLongPress,
56 onClick = {},
57 interactionSource = interactionSource,
58 // Passing null for the indication removes the ripple effect.
59 indication = null,
60 )
61 .pointerInput(settingsMenuBounds) {
62 awaitEachGesture {
63 val pointerInputChange = awaitFirstDown()
64 if (settingsMenuBounds?.contains(pointerInputChange.position) == false) {
65 viewModel.onTouchedOutside()
66 }
67 }
68 },
69 ) {
70 content(setSettingsMenuBounds)
71 }
72 }
73