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

Commit 99ade1fb authored by Ivan Chiang's avatar Ivan Chiang Committed by Android (Google) Code Review
Browse files

Merge "Fix A11y issue of chips and support reorder animation in RTL"

parents e092d2df 329131e7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
    android:checkable="true"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/search_chip_spacing"
    android:textColor="@color/search_chip_text_color"
    app:checkedIcon="@drawable/ic_check"
    app:chipBackgroundColor="@color/search_chip_background_color"
+3 −7
Original line number Diff line number Diff line
@@ -16,19 +16,15 @@

<HorizontalScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:overScrollMode="never"
    android:scrollbars="none">
    <com.google.android.material.chip.ChipGroup
    <LinearLayout
        android:id="@+id/search_chip_group"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/search_chip_group_margin"
        android:layout_marginBottom="@dimen/search_chip_group_margin"
        android:paddingStart="@dimen/search_chip_spacing"
        android:paddingEnd="@dimen/search_chip_spacing"
        android:gravity="center"
        app:chipSpacing="@dimen/search_chip_spacing"
        app:singleLine="true"/>
        android:paddingEnd="@dimen/search_chip_spacing"/>
</HorizontalScrollView>
 No newline at end of file
+2 −2
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.CallSuper;
@@ -71,7 +72,6 @@ import com.android.documentsui.sorting.SortController;
import com.android.documentsui.sorting.SortModel;

import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.chip.ChipGroup;

import java.util.ArrayList;
import java.util.Date;
@@ -195,7 +195,7 @@ public abstract class BaseActivity
                        mInjector.debugHelper::toggleDebugMode,
                        cmdInterceptor);

        ChipGroup chipGroup = findViewById(R.id.search_chip_group);
        ViewGroup chipGroup = findViewById(R.id.search_chip_group);
        mSearchManager = new SearchViewManager(searchListener, queryInterceptor,
                chipGroup, icicle);
        mSearchManager.updateChips(getCurrentRoot().derivedMimeTypes);
+85 −28
Original line number Diff line number Diff line
@@ -21,9 +21,11 @@ import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.android.documentsui.IconUtils;
@@ -32,7 +34,6 @@ import com.android.documentsui.base.MimeTypes;
import com.android.documentsui.base.Shared;

import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.common.primitives.Ints;

import java.util.ArrayList;
@@ -67,7 +68,7 @@ public class SearchChipViewManager {

    private static final Map<Integer, SearchChipData> sChipItems = new HashMap<>();

    private final ChipGroup mChipGroup;
    private final ViewGroup mChipGroup;
    private SearchChipViewManagerListener mListener;

    @VisibleForTesting
@@ -85,9 +86,7 @@ public class SearchChipViewManager {
                new SearchChipData(TYPE_VIDEOS, R.string.chip_title_videos, VIDEOS_MIMETYPES));
    }



    public SearchChipViewManager(@NonNull ChipGroup chipGroup) {
    public SearchChipViewManager(@NonNull ViewGroup chipGroup) {
        mChipGroup = chipGroup;
    }

@@ -191,7 +190,7 @@ public class SearchChipViewManager {
                mChipGroup.addView(chip);
            }
        }
        reorderCheckedChips(false /* hasAnim */);
        reorderCheckedChips(null /* clickedChip */, false /* hasAnim */);
    }


@@ -206,7 +205,6 @@ public class SearchChipViewManager {

    private static void setChipChecked(Chip chip, boolean isChecked) {
        chip.setChecked(isChecked);
        chip.setCheckedIconVisible(isChecked);
        chip.setChipIconVisible(!isChecked);
    }

@@ -224,6 +222,13 @@ public class SearchChipViewManager {

    private void onChipClick(View v) {
        final Chip chip = (Chip) v;

        // We need to show/hide the chip icon in our design.
        // When we show/hide the chip icon or do reorder animation,
        // the ripple effect will be interrupted. So, skip ripple
        // effect when the chip is clicked.
        chip.getBackground().setVisible(false /* visible */, false /* restart */);

        final SearchChipData item = (SearchChipData) chip.getTag();
        if (chip.isChecked()) {
            mCheckedChipItems.add(item);
@@ -232,7 +237,8 @@ public class SearchChipViewManager {
        }

        setChipChecked(chip, chip.isChecked());
        reorderCheckedChips(true /* hasAnim */);
        reorderCheckedChips(chip, true /* hasAnim */);

        if (mListener != null) {
            mListener.onChipCheckStateChanged();
        }
@@ -254,45 +260,96 @@ public class SearchChipViewManager {
    /**
     * Reorder the chips in chip group. The checked chip has higher order.
     *
     * @param clickedChip the clicked chip, may be null.
     * @param hasAnim if true, play move animation. Otherwise, not.
     */
    private void reorderCheckedChips(boolean hasAnim) {
    private void reorderCheckedChips(@Nullable Chip clickedChip, boolean hasAnim) {
        final ArrayList<Chip> chipList = new ArrayList<>();
        final int count = mChipGroup.getChildCount();
        final boolean playAnimation = hasAnim && mChipGroup.isAttachedToWindow();
        final Map<String, Float> originalXList = new HashMap<>();

        // if the size of chips is less than 2, no need to reorder chips
        if (count < 2) {
            return;
        }

        Chip item;
        // get the default order
        for (int i = 0; i < count; i++) {
            item = (Chip) mChipGroup.getChildAt(i);
            chipList.add(item);
            if (playAnimation) {
                originalXList.put(item.getText().toString(), item.getX());
            }
        }

        final int chipSpacing = mChipGroup.getChipSpacingHorizontal();
        float lastX = chipList.get(0).getX();
        // sort chips
        Collections.sort(chipList, CHIP_COMPARATOR);

        mChipGroup.removeAllViews();
        if (isChipOrderMatched(mChipGroup, chipList)) {
            // the order of chips is not changed
            return;
        }

        final int chipSpacing = mChipGroup.getPaddingEnd();
        final boolean isRtl = mChipGroup.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
        float lastX = isRtl ? mChipGroup.getWidth() - chipSpacing : chipSpacing;

        // remove all chips except current clicked chip to avoid losing
        // accessibility focus.
        for (int i = count - 1; i >= 0; i--) {
            item = (Chip) mChipGroup.getChildAt(i);
            if (!item.equals(clickedChip)) {
                mChipGroup.removeView(item);
            }
        }

        // add sorted chips
        for (int i = 0; i < count; i++) {
            item = chipList.get(i);
            if (!item.equals(clickedChip)) {
                mChipGroup.addView(item, i);
            }
        }

        if (hasAnim && mChipGroup.isAttachedToWindow()) {
            // start animation
            for (Chip chip : chipList) {
            mChipGroup.addView(chip);
            if (playAnimation) {
                ObjectAnimator animator = ObjectAnimator.ofFloat(chip, "x",
                        originalXList.get(chip.getText().toString()), lastX);
                if (isRtl) {
                    lastX -= chip.getMeasuredWidth();
                }

                ObjectAnimator animator = ObjectAnimator.ofFloat(chip, "x", chip.getX(), lastX);

                if (isRtl) {
                    lastX -= chipSpacing;
                } else {
                    lastX += chip.getMeasuredWidth() + chipSpacing;
                }
                animator.setDuration(CHIP_MOVE_ANIMATION_DURATION);
                animator.start();
            }
            lastX += chipSpacing + chip.getMeasuredWidth();
        }

        if (playAnimation) {
            // Let the first checked chip can be seen.
            // Let the first checked chip can be shown.
            View parent = (View) mChipGroup.getParent();
            if (parent != null && parent instanceof HorizontalScrollView) {
                ((HorizontalScrollView) mChipGroup.getParent()).smoothScrollTo(0, 0);
            if (parent instanceof HorizontalScrollView) {
                final int scrollToX = isRtl ? parent.getWidth() : 0;
                ((HorizontalScrollView) parent).smoothScrollTo(scrollToX, 0);
            }
        }
    }

    private static boolean isChipOrderMatched(ViewGroup chipGroup, ArrayList<Chip> chipList) {
        if (chipGroup == null || chipList == null) {
            return false;
        }

        final int chipCount = chipList.size();
        if (chipGroup.getChildCount() != chipCount) {
            return false;
        }
        for (int i = 0; i < chipCount; i++) {
            if (!chipList.get(i).equals(chipGroup.getChildAt(i))) {
                return false;
            }
        }
        return true;
    }

    /**
+2 −3
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.view.MenuItem.OnActionExpandListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.ViewGroup;

import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
@@ -50,8 +51,6 @@ import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;

import com.google.android.material.chip.ChipGroup;

import java.util.Timer;
import java.util.TimerTask;

@@ -91,7 +90,7 @@ public class SearchViewManager implements
    public SearchViewManager(
            SearchManagerListener listener,
            EventHandler<String> commandProcessor,
            ChipGroup chipGroup,
            ViewGroup chipGroup,
            @Nullable Bundle savedState) {
        this(listener, commandProcessor, new SearchChipViewManager(chipGroup), savedState,
                new Timer(), new Handler(Looper.getMainLooper()));
Loading