1 /*
<lambda>null2 * 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 package com.android.permissioncontroller.wear.permission.components
17
18 import android.app.Activity
19 import android.content.Context
20 import android.content.ContextWrapper
21 import androidx.compose.foundation.layout.BoxScope
22 import androidx.compose.runtime.Composable
23 import androidx.compose.runtime.LaunchedEffect
24 import androidx.compose.runtime.getValue
25 import androidx.compose.runtime.mutableStateOf
26 import androidx.compose.runtime.remember
27 import androidx.compose.runtime.setValue
28 import androidx.compose.ui.platform.LocalContext
29 import androidx.fragment.app.FragmentActivity
30 import androidx.wear.compose.foundation.ExpandableState
31 import androidx.wear.compose.foundation.SwipeToDismissValue
32 import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
33 import androidx.wear.compose.material.SwipeToDismissBox
34 import com.android.permissioncontroller.wear.permission.components.material3.WearPermissionScaffold
35 import com.android.permissioncontroller.wear.permission.components.theme.ResourceHelper
36 import com.android.permissioncontroller.wear.permission.components.theme.WearPermissionMaterialUIVersion
37
38 /**
39 * Screen that contains a list of items defined using the [content] parameter, adds the time text
40 * (if [showTimeText] is true), the tile (if [title] is not null), the vignette and the position
41 * indicator. It also manages the scaling animation and allows the user to scroll the content using
42 * the crown.
43 */
44 @Composable
45 fun ScrollableScreen(
46 materialUIVersion: WearPermissionMaterialUIVersion = ResourceHelper.materialUIVersionInSettings,
47 asScalingList: Boolean = false,
48 showTimeText: Boolean = true,
49 title: String? = null,
50 subtitle: CharSequence? = null,
51 image: Any? = null,
52 isLoading: Boolean = false,
53 titleTestTag: String? = null,
54 subtitleTestTag: String? = null,
55 content: ListScopeWrapper.() -> Unit,
56 ) {
57 var dismissed by remember { mutableStateOf(false) }
58 val activity = LocalContext.current.findActivity()
59 val state = rememberSwipeToDismissBoxState()
60
61 LaunchedEffect(state.currentValue) {
62 // If the swipe is complete
63 if (state.currentValue == SwipeToDismissValue.Dismissed) {
64 // pop the top fragment immediately or dismiss activity.
65 dismiss(activity)
66 // Set dismissed state as true
67 dismissed = true
68 // Set swipe box back to starting position(that is cancelled swipe effect) to
69 // show loading indicator while fragment dismisses.
70 // For some reason fragment `popBackImmediate` takes few secs at times.
71 state.snapTo(SwipeToDismissValue.Default)
72 }
73 }
74
75 if (getBackStackEntryCount(activity) > 0) {
76 SwipeToDismissBox(state = state) { isBackground ->
77 WearPermissionScaffold(
78 materialUIVersion,
79 asScalingList,
80 showTimeText,
81 title,
82 subtitle,
83 image,
84 isLoading = isLoading || isBackground || dismissed,
85 content,
86 titleTestTag,
87 subtitleTestTag,
88 )
89 }
90 } else {
91 WearPermissionScaffold(
92 materialUIVersion,
93 asScalingList,
94 showTimeText,
95 title,
96 subtitle,
97 image,
98 isLoading,
99 content,
100 titleTestTag,
101 subtitleTestTag,
102 )
103 }
104 }
105
dismissnull106 fun dismiss(activity: Activity) {
107 if (activity is FragmentActivity) {
108 if (!activity.supportFragmentManager.popBackStackImmediate()) {
109 activity.finish()
110 }
111 } else {
112 activity.finish()
113 }
114 }
115
getBackStackEntryCountnull116 internal fun getBackStackEntryCount(activity: Activity): Int {
117 return if (activity is FragmentActivity) {
118 activity.supportFragmentManager.primaryNavigationFragment
119 ?.childFragmentManager
120 ?.backStackEntryCount ?: 0
121 } else {
122 0
123 }
124 }
125
Contextnull126 fun Context.findActivity(): Activity {
127 var context = this
128 while (context is ContextWrapper) {
129 if (context is Activity) return context
130 context = context.baseContext
131 }
132 throw IllegalStateException("The screen should be called in the context of an Activity")
133 }
134
135 interface ListScopeWrapper {
itemnull136 fun item(key: Any? = null, contentType: Any? = null, content: @Composable () -> Unit)
137
138 fun items(
139 count: Int,
140 key: ((index: Int) -> Any)? = null,
141 contentType: (index: Int) -> Any? = { null },
142 content: @Composable (index: Int) -> Unit,
143 )
144
expandableItemsnull145 fun expandableItems(
146 state: ExpandableState,
147 count: Int,
148 key: ((index: Int) -> Any)? = null,
149 itemContent: @Composable BoxScope.(index: Int) -> Unit,
150 )
151
152 fun expandableButton(state: ExpandableState, key: Any? = null, content: @Composable () -> Unit)
153 }
154