Add the new migration tooltip for accessibility floating menu.(2/n)

Patch Action:
1.Linkify the text related to migration tooltip.
2.Create the component name of the sub setting of
Accessibility button let framework side could launch to
the corresponding setting page.

Bug: 175365399
Test: atest AccessibilityShortcutControllerTest AnnotationLinkSpanTest AccessibilityFloatingMenuTooltipViewTest MigrationTooltipViewTest
Change-Id: I2af5aaa5cf1789f406f423c64c91f0e23c31e6eb
This commit is contained in:
Peter_Liang
2021-03-27 00:55:47 +08:00
parent 610c0142d3
commit 0ca995f360
6 changed files with 284 additions and 2 deletions

View File

@@ -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)

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}