summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt90
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt42
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearGrantPermissionsViewModel.kt2
5 files changed, 127 insertions, 11 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
index 60e507433..cc32c5f02 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
@@ -169,8 +169,7 @@ public class GrantPermissionsWearViewHandler implements GrantPermissionsViewHand
mViewModel.getIconLiveData().setValue(
mGroupIcon == null ? null : mGroupIcon.loadDrawable(mActivity));
mViewModel.getGroupMessageLiveData().setValue(mGroupMessage.toString());
- mViewModel.getDetailMessageLiveData().setValue(
- mDetailMessage == null ? null : mDetailMessage.toString());
+ mViewModel.getDetailMessageLiveData().setValue(mDetailMessage);
int numButtons = BUTTON_RES_ID_TO_NUM.size();
List<Boolean> buttonVisibilityList = Arrays.asList(new Boolean[NEXT_BUTTON]);
for (int i = 0; i < numButtons; i++) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt
index 80cc52d7d..950353f52 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt
@@ -62,6 +62,7 @@ fun WearGrantPermissionsScreen(
title = groupMessage.value,
subtitle = detailMessage.value,
titleTestTag = "com.android.permissioncontroller:id/permission_message",
+ subtitleTestTag = "com.android.permissioncontroller:id/detail_message",
) {
if (
locationVisibilities.value.getOrElse(LOCATION_ACCURACY_LAYOUT) { false } &&
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt
new file mode 100644
index 000000000..aca39b497
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import android.text.Spanned
+import android.text.style.ClickableSpan
+import android.view.View
+import androidx.compose.foundation.text.ClickableText
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.wear.compose.material.MaterialTheme
+
+const val CLICKABLE_SPAN_TAG = "CLICKABLE_SPAN_TAG"
+
+@Composable
+fun AnnotatedText(text: CharSequence, style: TextStyle, modifier: Modifier = Modifier) {
+ val onClickCallbacks = mutableMapOf<String, (View) -> Unit>()
+ val annotatedString = spannableStringToAnnotatedString(text, onClickCallbacks)
+ val context = LocalContext.current
+ ClickableText(text = annotatedString, style = style, modifier = modifier) { offset ->
+ // Fires the onClickCallback at the clicked position.
+ // It's tricky to send an empty view over the parameter, but no way to get the proper one.
+ // Need to improve to use it in common.
+ annotatedString
+ .getStringAnnotations(CLICKABLE_SPAN_TAG, offset, offset)
+ .firstOrNull()
+ ?.let { onClickCallbacks.get(it.item)?.invoke(View(context)) }
+ }
+}
+
+@Composable
+private fun spannableStringToAnnotatedString(
+ text: CharSequence,
+ onClickCallbacks: MutableMap<String, (View) -> Unit>,
+ spanColor: Color = MaterialTheme.colors.primary
+) =
+ if (text is Spanned) {
+ buildAnnotatedString {
+ append((text.toString()))
+ for (span in text.getSpans(0, text.length, Any::class.java)) {
+ val start = text.getSpanStart(span)
+ val end = text.getSpanEnd(span)
+ when (span) {
+ is ClickableSpan ->
+ addClickableSpan(span, spanColor, start, end, onClickCallbacks)
+ else -> addStyle(SpanStyle(), start, end)
+ }
+ }
+ }
+ } else {
+ AnnotatedString(text.toString())
+ }
+
+private fun AnnotatedString.Builder.addClickableSpan(
+ span: ClickableSpan,
+ spanColor: Color,
+ start: Int,
+ end: Int,
+ onClickCallbacks: MutableMap<String, (View) -> Unit>
+) {
+ addStyle(
+ SpanStyle(color = spanColor, textDecoration = TextDecoration.Underline),
+ start,
+ end,
+ )
+ val key = "${CLICKABLE_SPAN_TAG}:$start:$end"
+ onClickCallbacks[key] = span::onClick
+ addStringAnnotation(CLICKABLE_SPAN_TAG, key, start, end)
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt
index 1a899c7bb..df7a56464 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt
@@ -78,10 +78,11 @@ import kotlinx.coroutines.launch
fun ScrollableScreen(
showTimeText: Boolean = true,
title: String? = null,
- subtitle: String? = null,
+ subtitle: CharSequence? = null,
image: Any? = null,
isLoading: Boolean = false,
titleTestTag: String? = null,
+ subtitleTestTag: String? = null,
content: ScalingLazyListScope.() -> Unit,
) {
var dismissed by remember { mutableStateOf(false) }
@@ -99,11 +100,29 @@ fun ScrollableScreen(
if (isBackground || dismissed) {
Box(modifier = Modifier.fillMaxSize())
} else {
- Scaffold(showTimeText, title, subtitle, image, isLoading, content, titleTestTag)
+ Scaffold(
+ showTimeText,
+ title,
+ subtitle,
+ image,
+ isLoading,
+ content,
+ titleTestTag,
+ subtitleTestTag
+ )
}
}
} else {
- Scaffold(showTimeText, title, subtitle, image, isLoading, content, titleTestTag)
+ Scaffold(
+ showTimeText,
+ title,
+ subtitle,
+ image,
+ isLoading,
+ content,
+ titleTestTag,
+ subtitleTestTag
+ )
}
}
@@ -112,11 +131,12 @@ fun ScrollableScreen(
internal fun Scaffold(
showTimeText: Boolean,
title: String?,
- subtitle: String?,
+ subtitle: CharSequence?,
image: Any?,
isLoading: Boolean,
content: ScalingLazyListScope.() -> Unit,
titleTestTag: String? = null,
+ subtitleTestTag: String? = null,
) {
val focusRequester = remember { FocusRequester() }
val listState = remember { ScalingLazyListState(initialCenterItemIndex = 0) }
@@ -196,11 +216,17 @@ internal fun Scaffold(
}
if (subtitle != null) {
item {
- Text(
+ var modifier: Modifier = Modifier
+ if (subtitleTestTag != null) {
+ modifier = modifier.testTag(subtitleTestTag)
+ }
+ AnnotatedText(
text = subtitle,
- style = MaterialTheme.typography.body2,
- modifier = Modifier.fillMaxWidth(),
- textAlign = TextAlign.Center
+ style =
+ MaterialTheme.typography.body2.copy(
+ color = MaterialTheme.colors.onSurfaceVariant
+ ),
+ modifier = modifier.fillMaxWidth(),
)
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearGrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearGrantPermissionsViewModel.kt
index 70c66373d..54a6e7c9f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearGrantPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearGrantPermissionsViewModel.kt
@@ -32,7 +32,7 @@ class WearGrantPermissionsViewModel : ViewModel() {
val groupMessageLiveData = MutableLiveData<String>()
/** A livedata which stores the permission group detail message. */
- val detailMessageLiveData = MutableLiveData<String?>()
+ val detailMessageLiveData = MutableLiveData<CharSequence?>()
/** A livedata which stores the permission group location-granularity visibilities. */
val locationVisibilitiesLiveData = MutableLiveData<List<Boolean>>()