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

Commit 1ae7a501 authored by Winson Chung's avatar Winson Chung
Browse files

Moving prediction icons back into recycler view.

- Fixes two accessibility issues with the prediction bar being
  separate from the rest of the list of icons related to scrolling
  and switch access.

Bug: 21725276
Change-Id: Ibc48e5028cace9a77000e6646b951f021910465d
parent 1406fd80
Loading
Loading
Loading
Loading
+0 −12
Original line number Diff line number Diff line
@@ -35,16 +35,4 @@
        android:focusable="true"
        android:descendantFocusability="afterDescendants" />

    <LinearLayout
        android:id="@+id/prediction_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/all_apps_prediction_bar_top_padding"
        android:paddingBottom="@dimen/all_apps_prediction_bar_bottom_padding"
        android:orientation="horizontal"
        android:focusable="true"
        android:descendantFocusability="afterDescendants"
        android:visibility="invisible" >
    </LinearLayout>

</com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
 No newline at end of file
+3 −2
Original line number Diff line number Diff line
@@ -18,10 +18,11 @@
    xmlns:launcher="http://schemas.android.com/apk/res-auto"
    style="@style/Icon.AllApps"
    android:id="@+id/icon"
    android:layout_width="0dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_weight="1"
    android:paddingTop="@dimen/all_apps_prediction_icon_top_padding"
    android:paddingBottom="@dimen/all_apps_prediction_icon_bottom_padding"
    android:focusable="true"
    android:background="@drawable/focusable_view_bg"
    launcher:iconDisplay="all_apps" />
+3 −3
Original line number Diff line number Diff line
@@ -70,9 +70,9 @@
    <dimen name="all_apps_search_bar_prediction_bar_padding">8dp</dimen>
    <dimen name="all_apps_icon_top_bottom_padding">8dp</dimen>
    <dimen name="all_apps_icon_width_gap">24dp</dimen>
    <!-- The top padding should account for the general all_apps_list_top_bottom_padding -->
    <dimen name="all_apps_prediction_bar_top_padding">0dp</dimen>
    <dimen name="all_apps_prediction_bar_bottom_padding">16dp</dimen>
    <!-- The top padding should account for the existing all_apps_list_top_bottom_padding -->
    <dimen name="all_apps_prediction_icon_top_padding">8dp</dimen>
    <dimen name="all_apps_prediction_icon_bottom_padding">18dp</dimen>
    <dimen name="all_apps_list_top_bottom_padding">8dp</dimen>

<!-- Widget tray -->
+15 −236
Original line number Diff line number Diff line
@@ -75,7 +75,11 @@ final class FullMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm {
    public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
           AlphabeticalAppsList.SectionInfo withSection,
           int sectionAppCount, int numAppsPerRow, int mergeCount) {
        // Merge EVERYTHING
        // Don't merge the predicted apps
        if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
            return false;
        }
        // Otherwise, merge every other section
        return true;
    }
}
@@ -103,6 +107,11 @@ final class SimpleSectionMergeAlgorithm implements AlphabeticalAppsList.MergeAlg
    public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
           AlphabeticalAppsList.SectionInfo withSection,
           int sectionAppCount, int numAppsPerRow, int mergeCount) {
        // Don't merge the predicted apps
        if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
            return false;
        }

        // Continue merging if the number of hanging apps on the final row is less than some
        // fixed number (ragged), the merged rows has yet to exceed some minimum row count,
        // and while the number of merged sections is less than some fixed number of merges
@@ -127,17 +136,14 @@ final class SimpleSectionMergeAlgorithm implements AlphabeticalAppsList.MergeAlg
 * The all apps view container.
 */
public class AllAppsContainerView extends BaseContainerView implements DragSource,
        LauncherTransitionable, AlphabeticalAppsList.AdapterChangedCallback,
        AllAppsGridAdapter.PredictionBarSpacerCallbacks, View.OnTouchListener,
        View.OnLongClickListener, ViewTreeObserver.OnPreDrawListener,
        AllAppsSearchBarController.Callbacks, Stats.LaunchSourceProvider {
        LauncherTransitionable, View.OnTouchListener, View.OnLongClickListener,
        AllAppsSearchBarController.Callbacks {

    private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
    private static final int MAX_NUM_MERGES_PHONE = 2;

    @Thunk Launcher mLauncher;
    @Thunk AlphabeticalAppsList mApps;
    private LayoutInflater mLayoutInflater;
    private AllAppsGridAdapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    private RecyclerView.ItemDecoration mItemDecoration;
@@ -146,7 +152,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
    @Thunk View mContainerView;
    @Thunk View mRevealView;
    @Thunk AllAppsRecyclerView mAppsRecyclerView;
    @Thunk ViewGroup mPredictionBarView;
    @Thunk AllAppsSearchBarController mSearchBarController;
    private ViewGroup mSearchBarContainerView;
    private View mSearchBarView;
@@ -159,18 +164,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
    private final Point mBoundsCheckLastTouchDownPos = new Point(-1, -1);
    // This coordinate is relative to its parent
    private final Point mIconLastTouchPos = new Point();
    // This coordinate is used to proxy click and long-click events to the prediction bar icons
    private final Point mPredictionIconTouchDownPos = new Point();
    // Normal container insets
    private int mPredictionBarHeight;
    private int mLastRecyclerViewScrollPos = -1;
    @Thunk boolean mFocusPredictionBarOnFirstBind;

    private SpannableStringBuilder mSearchQueryBuilder = null;

    private CheckLongPressHelper mPredictionIconCheckForLongPress;
    private View mPredictionIconUnderTouch;

    public AllAppsContainerView(Context context) {
        this(context, null);
    }
@@ -184,19 +180,10 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
        Resources res = context.getResources();

        mLauncher = (Launcher) context;
        mLayoutInflater = LayoutInflater.from(context);
        DeviceProfile grid = mLauncher.getDeviceProfile();
        mPredictionBarHeight = (int) (grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx +
                Utilities.calculateTextHeight(grid.allAppsIconTextSizePx) +
                2 * res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding) +
                res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_padding) +
                res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding));
        mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
        mApps = new AlphabeticalAppsList(context);
        mApps.setAdapterChangedCallback(this);
        mAdapter = new AllAppsGridAdapter(context, mApps, this, this, mLauncher, this);
        mAdapter = new AllAppsGridAdapter(context, mApps, this, mLauncher, this);
        mAdapter.setEmptySearchText(res.getString(R.string.all_apps_loading_message));
        mAdapter.setPredictionRowHeight(mPredictionBarHeight);
        mApps.setAdapter(mAdapter);
        mLayoutManager = mAdapter.getLayoutManager();
        mItemDecoration = mAdapter.getItemDecoration();
@@ -310,19 +297,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    if (!mApps.getPredictedApps().isEmpty()) {
                        // If the prediction bar is going to be bound, then defer focusing until
                        // it is first bound
                        if (mPredictionBarView.getChildCount() == 0) {
                            mFocusPredictionBarOnFirstBind = true;
                        } else {
                            mPredictionBarView.requestFocus();
                        }
                    } else {
                    mAppsRecyclerView.requestFocus();
                }
            }
            }
        };
        mSearchBarContainerView = (ViewGroup) findViewById(R.id.search_box_container);
        mSearchBarContainerView.setOnFocusChangeListener(focusProxyListener);
@@ -333,7 +310,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
        // Load the all apps recycler view
        mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
        mAppsRecyclerView.setApps(mApps);
        mAppsRecyclerView.setPredictionBarHeight(mPredictionBarHeight);
        mAppsRecyclerView.setLayoutManager(mLayoutManager);
        mAppsRecyclerView.setAdapter(mAdapter);
        mAppsRecyclerView.setHasFixedSize(true);
@@ -341,11 +317,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
            mAppsRecyclerView.addItemDecoration(mItemDecoration);
        }

        // Fix the prediction bar height
        mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar);
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
        lp.height = mPredictionBarHeight;

        updateBackgroundAndPaddings();
    }

@@ -354,49 +325,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
        mLauncher.updateOverlayBounds(newBounds);
    }

    @Override
    public void onBindPredictionBar() {
        updatePredictionBarVisibility();

        List<AppInfo> predictedApps = mApps.getPredictedApps();

        // Remove extra prediction icons
        while (mPredictionBarView.getChildCount() > mNumPredictedAppsPerRow) {
            mPredictionBarView.removeViewAt(mPredictionBarView.getChildCount() - 1);
        }

        int childCount = mPredictionBarView.getChildCount();
        for (int i = 0; i < mNumPredictedAppsPerRow; i++) {
            BubbleTextView icon;
            if (i < childCount) {
                // If a child at that index exists, then get that child
                icon = (BubbleTextView) mPredictionBarView.getChildAt(i);
            } else {
                // Otherwise, inflate a new icon
                icon = (BubbleTextView) mLayoutInflater.inflate(
                        R.layout.all_apps_prediction_bar_icon, mPredictionBarView, false);
                icon.setFocusable(true);
                icon.setLongPressTimeout(ViewConfiguration.get(getContext()).getLongPressTimeout());
                mPredictionBarView.addView(icon);
            }

            // Either apply the app info to the child, or hide the view
            if (i < predictedApps.size()) {
                if (icon.getVisibility() != View.VISIBLE) {
                    icon.setVisibility(View.VISIBLE);
                }
                icon.applyFromApplicationInfo(predictedApps.get(i));
            } else {
                icon.setVisibility(View.INVISIBLE);
            }
        }

        if (mFocusPredictionBarOnFirstBind) {
            mFocusPredictionBarOnFirstBind = false;
            mPredictionBarView.requestFocus();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Update the number of items in the grid before we measure the view
@@ -476,22 +404,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
            lp.rightMargin = (getMeasuredWidth() - searchBarBounds.right) - backgroundPadding.right;
            mSearchBarContainerView.requestLayout();
        }

        // Update the prediction bar insets as well
        mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar);
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
        lp.leftMargin = padding.left + mAppsRecyclerView.getMaxScrollbarWidth();
        lp.rightMargin = padding.right + mAppsRecyclerView.getMaxScrollbarWidth();
        mPredictionBarView.requestLayout();
    }

    @Override
    public boolean onPreDraw() {
        if (mNumAppsPerRow > 0) {
            // Update the position of the prediction bar to match the scroll of the all apps list
            synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition());
        }
        return true;
    }

    @Override
@@ -620,18 +532,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
        }
    }

    @Override
    public void onAdapterItemsChanged() {
        updatePredictionBarVisibility();
    }

    @Override
    public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
        // Register for a pre-draw listener to synchronize the recycler view scroll to other views
        // in this container
        if (!toWorkspace) {
            getViewTreeObserver().addOnPreDrawListener(this);
        }
        // Do nothing
    }

    @Override
@@ -647,37 +550,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
    @Override
    public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
        if (toWorkspace) {
            getViewTreeObserver().removeOnPreDrawListener(this);
            mLastRecyclerViewScrollPos = -1;

            // Reset the search bar after transitioning home
            mSearchBarController.reset();
        }
    }

    /**
     * Updates the container when the recycler view is scrolled.
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void synchronizeToRecyclerViewScrollPosition(int scrollY) {
        if (mLastRecyclerViewScrollPos != scrollY) {
            mLastRecyclerViewScrollPos = scrollY;

            // Scroll the prediction bar with the contents of the recycler view
            mPredictionBarView.setTranslationY(-scrollY + mAppsRecyclerView.getPaddingTop());
        }
    }

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        // If we were waiting for long-click, cancel the request once a child has started handling
        // the scrolling
        if (mPredictionIconCheckForLongPress != null) {
            mPredictionIconCheckForLongPress.cancelLongPress();
        }
        super.requestDisallowInterceptTouchEvent(disallowIntercept);
    }

    /**
     * Handles the touch events to dismiss all apps when clicking outside the bounds of the
     * recycler view.
@@ -689,19 +566,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // We workaround the fact that the recycler view needs the touches for the scroll
                // and we want to intercept it for clicks in the prediction bar by handling clicks
                // and long clicks in the prediction bar ourselves.
                if (mPredictionBarView != null && mPredictionBarView.getVisibility() == View.VISIBLE) {
                    mPredictionIconTouchDownPos.set(x, y);
                    mPredictionIconUnderTouch = findPredictedAppAtCoordinate(x, y);
                    if (mPredictionIconUnderTouch != null) {
                        mPredictionIconCheckForLongPress =
                                new CheckLongPressHelper(mPredictionIconUnderTouch, this);
                        mPredictionIconCheckForLongPress.postCheckForLongPress();
                    }
                }

                if (!mContentBounds.isEmpty()) {
                    // Outset the fixed bounds and check if the touch is outside all apps
                    Rect tmpRect = new Rect(mContentBounds);
@@ -719,19 +583,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mPredictionIconUnderTouch != null) {
                    float dist = (float) Math.hypot(x - mPredictionIconTouchDownPos.x,
                            y - mPredictionIconTouchDownPos.y);
                    if (dist > ViewConfiguration.get(getContext()).getScaledTouchSlop()) {
                        if (mPredictionIconCheckForLongPress != null) {
                            mPredictionIconCheckForLongPress.cancelLongPress();
                        }
                        mPredictionIconCheckForLongPress = null;
                        mPredictionIconUnderTouch = null;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mBoundsCheckLastTouchDownPos.x > -1) {
                    ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
@@ -745,26 +596,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
                        return true;
                    }
                }

                // Trigger the click on the prediction bar icon if that's where we touched
                if (mPredictionIconUnderTouch != null &&
                        !mPredictionIconCheckForLongPress.hasPerformedLongPress()) {
                    mLauncher.onClick(mPredictionIconUnderTouch);
                }

                // Fall through
            case MotionEvent.ACTION_CANCEL:
                mBoundsCheckLastTouchDownPos.set(-1, -1);
                mPredictionIconTouchDownPos.set(-1, -1);

                // On touch up/cancel, cancel the long press on the prediction bar icon if it has
                // not yet been performed
                if (mPredictionIconCheckForLongPress != null) {
                    mPredictionIconCheckForLongPress.cancelLongPress();
                    mPredictionIconCheckForLongPress = null;
                }
                mPredictionIconUnderTouch = null;

                break;
        }
        return false;
@@ -792,59 +626,4 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
        mSearchQueryBuilder.clearSpans();
        Selection.setSelection(mSearchQueryBuilder, 0);
    }

    @Override
    public void fillInLaunchSourceData(Bundle sourceData) {
        // Since the other cases are caught by the AllAppsRecyclerView LaunchSourceProvider, we just
        // handle the prediction bar icons here
        sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS);
        sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
                Stats.SUB_CONTAINER_ALL_APPS_PREDICTION);
    }

    /**
     * Returns the predicted app in the prediction bar given a set of local coordinates.
     */
    private View findPredictedAppAtCoordinate(int x, int y) {
        Rect hitRect = new Rect();

        // Ensure that are touching in the recycler view
        int[] coord = {x, y};
        Utilities.mapCoordInSelfToDescendent(mAppsRecyclerView, this, coord);
        mAppsRecyclerView.getHitRect(hitRect);
        if (!hitRect.contains(coord[0], coord[1])) {
            return null;
        }

        // Check against the children of the prediction bar
        coord[0] = x;
        coord[1] = y;
        Utilities.mapCoordInSelfToDescendent(mPredictionBarView, this, coord);
        for (int i = 0; i < mPredictionBarView.getChildCount(); i++) {
            View child = mPredictionBarView.getChildAt(i);
            if (child.getVisibility() != View.VISIBLE) {
                continue;
            }
            child.getHitRect(hitRect);
            if (hitRect.contains(coord[0], coord[1])) {
                return child;
            }
        }
        return null;
    }

    /**
     * Updates the visibility of the prediction bar.
     * @return whether the prediction bar is visible
     */
    private boolean updatePredictionBarVisibility() {
        boolean showPredictionBar = !mApps.getPredictedApps().isEmpty() &&
                (!mApps.hasFilter() || mSearchBarController.shouldShowPredictionBar());
        if (showPredictionBar) {
            mPredictionBarView.setVisibility(View.VISIBLE);
        } else if (!showPredictionBar) {
            mPredictionBarView.setVisibility(View.INVISIBLE);
        }
        return showPredictionBar;
    }
}
+38 −53

File changed.

Preview size limit exceeded, changes collapsed.

Loading