Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ca995f3 authored by Peter_Liang's avatar Peter_Liang
Browse files

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
parent 610c0142
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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)
+91 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -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;
    }
+19 −2
Original line number Diff line number Diff line
@@ -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());
    }
}
+95 −0
Original line number Diff line number Diff line
/*
 * 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();
    }
}
Loading