Files
Lawnchair/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
T
Brandon Dayauon deb2e2c35e Make all apps text focusable by accessibility.
From b/330638194, the requirement is that
1. "all apps" can be focusable with talkback
2. "all apps" text is persisted when in accessibility mode.

Merged-In:Ibabb92bd9d202221fdc9cfacb585755167898187
bug:330638194
Test: manual video: https://drive.google.com/file/d/1QI_F4dUijfnU2ahE2Mg6yrmRJfOVyuVJ/view?usp=sharing
Flag: None

Change-Id: Ibabb92bd9d202221fdc9cfacb585755167898187
2024-05-10 18:19:27 +00:00

258 lines
8.7 KiB
Java

/*
* Copyright (C) 2019 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.launcher3.appprediction;
import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Typeface;
import android.os.Build;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
/**
* A view which shows a horizontal divider
*/
@TargetApi(Build.VERSION_CODES.O)
public class AppsDividerView extends View implements FloatingHeaderRow {
public enum DividerType {
NONE,
LINE,
ALL_APPS_LABEL
}
private final TextPaint mPaint = new TextPaint();
private DividerType mDividerType = DividerType.NONE;
private final @ColorInt int mStrokeColor;
private final @ColorInt int mAllAppsLabelTextColor;
private final AccessibilityManager mAccessibilityManager;
private Layout mAllAppsLabelLayout;
private boolean mShowAllAppsLabel;
private FloatingHeaderView mParent;
private boolean mTabsHidden;
private FloatingHeaderRow[] mRows = FloatingHeaderRow.NO_ROWS;
private boolean mIsScrolledOut = false;
private final int[] mDividerSize;
public AppsDividerView(Context context) {
this(context, null);
}
public AppsDividerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AppsDividerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDividerSize = new int[]{
getResources().getDimensionPixelSize(R.dimen.all_apps_divider_width),
getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height)
};
mStrokeColor = ContextCompat.getColor(context, R.color.material_color_outline_variant);
mAllAppsLabelTextColor = ContextCompat.getColor(context,
R.color.material_color_on_surface_variant);
mAccessibilityManager = AccessibilityManager.getInstance(context);
setShowAllAppsLabel(!ALL_APPS_VISITED_COUNT.hasReachedMax(context));
}
public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
mParent = parent;
mTabsHidden = tabsHidden;
mRows = rows;
updateDividerType();
}
/** {@code true} if all apps label should be shown in place of divider. */
public void setShowAllAppsLabel(boolean showAllAppsLabel) {
if (mAccessibilityManager.isEnabled() && !Utilities.isRunningInTestHarness()) {
showAllAppsLabel = true;
}
if (showAllAppsLabel != mShowAllAppsLabel) {
mShowAllAppsLabel = showAllAppsLabel;
updateDividerType();
}
}
@Override
public int getExpectedHeight() {
return getPaddingTop() + getPaddingBottom();
}
@Override
public boolean shouldDraw() {
return mDividerType != DividerType.NONE;
}
@Override
public boolean hasVisibleContent() {
return false;
}
private void updateDividerType() {
final DividerType dividerType;
if (!mTabsHidden) {
dividerType = DividerType.NONE;
} else {
// Check how many sections above me.
int sectionCount = 0;
for (FloatingHeaderRow row : mRows) {
if (row == this) {
break;
} else if (row.shouldDraw()) {
sectionCount++;
}
}
if (mShowAllAppsLabel && sectionCount > 0) {
dividerType = DividerType.ALL_APPS_LABEL;
} else if (sectionCount == 1) {
dividerType = DividerType.LINE;
} else {
dividerType = DividerType.NONE;
}
}
if (mDividerType != dividerType) {
mDividerType = dividerType;
int topPadding;
int bottomPadding;
setContentDescription(null);
switch (dividerType) {
case LINE:
topPadding = 0;
bottomPadding = getResources()
.getDimensionPixelSize(R.dimen.all_apps_prediction_row_divider_height);
mPaint.setColor(mStrokeColor);
break;
case ALL_APPS_LABEL:
topPadding = getAllAppsLabelLayout().getHeight() + getResources()
.getDimensionPixelSize(R.dimen.all_apps_label_top_padding);
bottomPadding = getResources()
.getDimensionPixelSize(R.dimen.all_apps_label_bottom_padding);
mPaint.setColor(mAllAppsLabelTextColor);
setContentDescription(mAllAppsLabelLayout.getText());
break;
case NONE:
default:
topPadding = bottomPadding = 0;
break;
}
setPadding(getPaddingLeft(), topPadding, getPaddingRight(), bottomPadding);
updateViewVisibility();
invalidate();
requestLayout();
if (mParent != null) {
mParent.onHeightUpdated();
}
}
}
private void updateViewVisibility() {
setVisibility(mDividerType == DividerType.NONE
? GONE
: (mIsScrolledOut ? INVISIBLE : VISIBLE));
}
@Override
protected void onDraw(Canvas canvas) {
if (mDividerType == DividerType.LINE) {
int l = (getWidth() - mDividerSize[0]) / 2;
int t = getHeight() - (getPaddingBottom() / 2);
int radius = mDividerSize[1];
canvas.drawRoundRect(l, t, l + mDividerSize[0], t + mDividerSize[1], radius, radius,
mPaint);
} else if (mDividerType == DividerType.ALL_APPS_LABEL) {
Layout textLayout = getAllAppsLabelLayout();
int x = getWidth() / 2 - textLayout.getWidth() / 2;
int y = getHeight() - getPaddingBottom() - textLayout.getHeight();
canvas.translate(x, y);
textLayout.draw(canvas);
canvas.translate(-x, -y);
}
}
private Layout getAllAppsLabelLayout() {
if (mAllAppsLabelLayout == null) {
mPaint.setAntiAlias(true);
mPaint.setTypeface(Typeface.create("google-sans", Typeface.NORMAL));
mPaint.setTextSize(
getResources().getDimensionPixelSize(R.dimen.all_apps_label_text_size));
CharSequence allAppsLabelText = getResources().getText(R.string.all_apps_label);
mAllAppsLabelLayout = StaticLayout.Builder.obtain(
allAppsLabelText, 0, allAppsLabelText.length(), mPaint,
Math.round(mPaint.measureText(allAppsLabelText.toString())))
.setAlignment(Layout.Alignment.ALIGN_CENTER)
.setMaxLines(1)
.setIncludePad(true)
.build();
}
return mAllAppsLabelLayout;
}
@Override
public boolean hasOverlappingRendering() {
return false;
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getPaddingBottom() + getPaddingTop());
}
@Override
public void setVerticalScroll(int scroll, boolean isScrolledOut) {
setTranslationY(scroll);
mIsScrolledOut = isScrolledOut;
updateViewVisibility();
}
@Override
public Class<AppsDividerView> getTypeClass() {
return AppsDividerView.class;
}
@Override
public View getFocusedChild() {
return null;
}
}