diff options
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>>() |