diff options
6 files changed, 284 insertions, 2 deletions
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index 2b4e09d66851..c1de2e403e1d 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -83,6 +83,11 @@ public class AccessibilityShortcutController { public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME = new ComponentName("com.android.server.accessibility", "ReduceBrightColors"); + // The component name for the sub setting of Accessibility button in Accessibility settings + public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME = + new ComponentName("com.android.server.accessibility", "AccessibilityButton"); + + private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY) diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java new file mode 100644 index 000000000000..d8e80fe99331 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 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.systemui.accessibility.floatingmenu; + +import android.text.Annotation; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.style.ClickableSpan; +import android.view.View; + +import androidx.annotation.NonNull; + +import java.util.Arrays; +import java.util.Optional; + +/** + * A span that turns the text wrapped by annotation tag into the clickable link text. + */ +class AnnotationLinkSpan extends ClickableSpan { + private final Optional<View.OnClickListener> mClickListener; + + private AnnotationLinkSpan(View.OnClickListener listener) { + mClickListener = Optional.ofNullable(listener); + } + + @Override + public void onClick(View view) { + mClickListener.ifPresent(listener -> listener.onClick(view)); + } + + /** + * Makes the text has the link with the click action. In addition, the span will match first + * LinkInfo and attach into the text. + * + * @param text the text wrapped by annotation tag + * @param linkInfos used to attach the click action into the corresponding span + * @return the text attached with the span + */ + static CharSequence linkify(CharSequence text, LinkInfo... linkInfos) { + final SpannableString msg = new SpannableString(text); + final Annotation[] spans = + msg.getSpans(/* queryStart= */ 0, msg.length(), Annotation.class); + final SpannableStringBuilder builder = new SpannableStringBuilder(msg); + + Arrays.asList(spans).forEach(annotationTag -> { + final String key = annotationTag.getValue(); + final Optional<LinkInfo> linkInfo = + Arrays.asList(linkInfos).stream().filter( + info -> info.mAnnotation.isPresent() + && info.mAnnotation.get().equals(key)).findFirst(); + + linkInfo.flatMap(info -> info.mListener).ifPresent(listener -> { + final AnnotationLinkSpan span = new AnnotationLinkSpan(listener); + builder.setSpan(span, + msg.getSpanStart(annotationTag), + msg.getSpanEnd(annotationTag), + msg.getSpanFlags(span)); + }); + }); + + return builder; + } + + /** + * Data class to store the annotation and the click action. + */ + static class LinkInfo { + static final String DEFAULT_ANNOTATION = "link"; + private final Optional<String> mAnnotation; + private final Optional<View.OnClickListener> mListener; + + LinkInfo(@NonNull String annotation, View.OnClickListener listener) { + mAnnotation = Optional.of(annotation); + mListener = Optional.ofNullable(listener); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java index 76aa1f14e58b..a5d5f1267eaa 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java @@ -31,6 +31,7 @@ import android.graphics.Rect; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.ShapeDrawable; import android.os.Bundle; +import android.text.method.MovementMethod; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.LayoutInflater; @@ -150,6 +151,10 @@ class BaseTooltipView extends FrameLayout { mTextView.setText(text); } + void setMovementMethod(MovementMethod movement) { + mTextView.setMovementMethod(movement); + } + private boolean isShowing() { return mIsShowing; } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java index 58f974bcfc59..3829330f5235 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java @@ -16,7 +16,12 @@ package com.android.systemui.accessibility.floatingmenu; +import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_BUTTON_COMPONENT_NAME; + import android.content.Context; +import android.content.Intent; +import android.provider.Settings; +import android.text.method.LinkMovementMethod; import com.android.systemui.R; @@ -28,7 +33,19 @@ class MigrationTooltipView extends BaseTooltipView { MigrationTooltipView(Context context, AccessibilityFloatingMenuView anchorView) { super(context, anchorView); - setDescription( - getResources().getString(R.string.accessibility_floating_button_migration_tooltip)); + final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Intent.EXTRA_COMPONENT_NAME, + ACCESSIBILITY_BUTTON_COMPONENT_NAME.flattenToShortString()); + final AnnotationLinkSpan.LinkInfo linkInfo = new AnnotationLinkSpan.LinkInfo( + AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION, + v -> { + getContext().startActivity(intent); + hide(); + }); + + final int textResId = R.string.accessibility_floating_button_migration_tooltip; + setDescription(AnnotationLinkSpan.linkify(getContext().getText(textResId), linkInfo)); + setMovementMethod(LinkMovementMethod.getInstance()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java new file mode 100644 index 000000000000..46c930f9a5eb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 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.systemui.accessibility.floatingmenu; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; + +import android.testing.AndroidTestingRunner; +import android.text.SpannableStringBuilder; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** Tests for {@link AnnotationLinkSpan}. */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class AnnotationLinkSpanTest extends SysuiTestCase { + + private AnnotationLinkSpan.LinkInfo mLinkInfo; + + @Before + public void setUp() { + mLinkInfo = new AnnotationLinkSpan.LinkInfo( + AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION, + mock(View.OnClickListener.class)); + } + + @Test + public void linkifyText_textAttachedWithSpan() { + final CharSequence text = getContext().getText( + R.string.accessibility_floating_button_migration_tooltip); + final SpannableStringBuilder builder = + (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, mLinkInfo); + final int AnnotationLinkSpanNum = + builder.getSpans(/* queryStart= */ 0, builder.length(), + AnnotationLinkSpan.class).length; + + assertThat(AnnotationLinkSpanNum).isEqualTo(1); + } + + @Test + public void linkifyText_withoutAnnotationTag_textWithoutSpan() { + final CharSequence text = "text without any annotation tag"; + final SpannableStringBuilder builder = + (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, mLinkInfo); + final int AnnotationLinkSpanNum = + builder.getSpans(/* queryStart= */ 0, builder.length(), + AnnotationLinkSpan.class).length; + + assertThat(AnnotationLinkSpanNum).isEqualTo(0); + } + + @Test + public void linkifyText_twoLinkInfoWithSameAnnotation_listenerInvoked() { + final AtomicBoolean isClicked = new AtomicBoolean(false); + final CharSequence text = getContext().getText( + R.string.accessibility_floating_button_migration_tooltip); + final View.OnClickListener firstListener = v -> isClicked.set(true); + final AnnotationLinkSpan.LinkInfo firstLinkInfo = new AnnotationLinkSpan.LinkInfo( + AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION, firstListener); + + final SpannableStringBuilder builder = + (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, firstLinkInfo, mLinkInfo); + final AnnotationLinkSpan[] firstAnnotationLinkSpan = + builder.getSpans(/* queryStart= */ 0, builder.length(), + AnnotationLinkSpan.class); + firstAnnotationLinkSpan[0].onClick(mock(View.class)); + + assertThat(isClicked.get()).isTrue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java new file mode 100644 index 000000000000..c5bd2fefae16 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 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.systemui.accessibility.floatingmenu; + +import static com.google.common.truth.Truth.assertThat; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.text.SpannableString; +import android.text.method.LinkMovementMethod; +import android.widget.TextView; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link MigrationTooltipView}. */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class MigrationTooltipViewTest extends SysuiTestCase { + + private TextView mTextView; + + @Before + public void setUp() { + final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext); + final MigrationTooltipView toolTipView = new MigrationTooltipView(mContext, menuView); + mTextView = toolTipView.findViewById(R.id.text); + } + + @Test + public void onCreate_setLinkMovementMethod() { + assertThat(mTextView.getMovementMethod()).isInstanceOf(LinkMovementMethod.class); + } + + @Test + public void onCreate_setDescription_matchTextAndSpanNum() { + final CharSequence expectedTextWithoutSpan = + AnnotationLinkSpan.linkify(mContext.getText( + R.string.accessibility_floating_button_migration_tooltip)).toString(); + final SpannableString spannableString = (SpannableString) mTextView.getText(); + final int AnnotationLinkSpanNum = + spannableString.getSpans(/* queryStart= */ 0, spannableString.length(), + AnnotationLinkSpan.class).length; + + assertThat(AnnotationLinkSpanNum).isEqualTo(1); + assertThat(mTextView.getText().toString().contentEquals(expectedTextWithoutSpan)).isTrue(); + } +} |