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:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user