Replace taskbar hotseat with real hotseat when folder is open
- Seamlessly show real hotseat and hide taskbar hotseat, while keeping rest of taskbar visible - Update MultiValueAlpha to allow for taking max alpha instead of blending, and use that for Hotseat - Fix folder open bounds on home screen when taskbar is present Test: Open folder from taskbar on home, can drag out items Bug: 182079330 Bug: 171917176 Change-Id: I7c1983e3219b1341cf233260f0ccac9051c4dc14
This commit is contained in:
parent
462384dbed
commit
ae72b46b2c
|
@ -25,7 +25,6 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/taskbar_background"
|
||||
android:gravity="center"
|
||||
android:animateLayoutChanges="true"/>
|
||||
android:gravity="center"/>
|
||||
|
||||
</com.android.launcher3.taskbar.TaskbarContainerView>
|
|
@ -315,6 +315,10 @@ public abstract class BaseQuickstepLauncher extends Launcher
|
|||
@Override
|
||||
public void onDragLayerHierarchyChanged() {
|
||||
onLauncherStateOrFocusChanged();
|
||||
|
||||
if (mTaskbarController != null) {
|
||||
mTaskbarController.onLauncherDragLayerHierarchyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,8 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
|||
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_REPLACE_TASKBAR_WITH_HOTSEAT;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
|
||||
|
@ -40,6 +42,7 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.BaseQuickstepLauncher;
|
||||
import com.android.launcher3.Hotseat;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.QuickstepAppTransitionManagerImpl;
|
||||
import com.android.launcher3.R;
|
||||
|
@ -144,22 +147,13 @@ public class TaskbarController {
|
|||
ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
|
||||
ActivityOptions.makeBasic());
|
||||
} else if (tag instanceof FolderInfo) {
|
||||
FolderIcon folderIcon = (FolderIcon) view;
|
||||
Folder folder = folderIcon.getFolder();
|
||||
|
||||
setTaskbarWindowFullscreen(true);
|
||||
|
||||
mTaskbarContainerView.post(() -> {
|
||||
folder.animateOpen();
|
||||
|
||||
folder.iterateOverItems((itemInfo, itemView) -> {
|
||||
itemView.setOnClickListener(getItemOnClickListener());
|
||||
itemView.setOnLongClickListener(getItemOnLongClickListener());
|
||||
// To play haptic when dragging, like other Taskbar items do.
|
||||
itemView.setHapticFeedbackEnabled(true);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
if (mLauncher.hasBeenResumed()) {
|
||||
FolderInfo folderInfo = (FolderInfo) tag;
|
||||
onClickedOnFolderFromHome(folderInfo);
|
||||
} else {
|
||||
FolderIcon folderIcon = (FolderIcon) view;
|
||||
onClickedOnFolderInApp(folderIcon);
|
||||
}
|
||||
} else {
|
||||
ItemClickHandler.INSTANCE.onClick(view);
|
||||
}
|
||||
|
@ -169,6 +163,34 @@ public class TaskbarController {
|
|||
};
|
||||
}
|
||||
|
||||
// Open the real folder in Launcher.
|
||||
private void onClickedOnFolderFromHome(FolderInfo folderInfo) {
|
||||
alignRealHotseatWithTaskbar();
|
||||
|
||||
FolderIcon folderIcon = (FolderIcon) mLauncher.getHotseat()
|
||||
.getFirstItemMatch((info, v) -> info == folderInfo);
|
||||
folderIcon.post(folderIcon::performClick);
|
||||
}
|
||||
|
||||
// Open the Taskbar folder, and handle clicks on folder items.
|
||||
private void onClickedOnFolderInApp(FolderIcon folderIcon) {
|
||||
Folder folder = folderIcon.getFolder();
|
||||
|
||||
setTaskbarWindowFullscreen(true);
|
||||
|
||||
mTaskbarContainerView.post(() -> {
|
||||
folder.animateOpen();
|
||||
|
||||
folder.iterateOverItems((itemInfo, itemView) -> {
|
||||
itemView.setOnClickListener(getItemOnClickListener());
|
||||
itemView.setOnLongClickListener(getItemOnLongClickListener());
|
||||
// To play haptic when dragging, like other Taskbar items do.
|
||||
itemView.setHapticFeedbackEnabled(true);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public View.OnLongClickListener getItemOnLongClickListener() {
|
||||
return view -> {
|
||||
|
@ -306,6 +328,7 @@ public class TaskbarController {
|
|||
mAnimator = createAnimToLauncher(null, duration);
|
||||
} else {
|
||||
mAnimator = createAnimToApp(duration);
|
||||
replaceTaskbarWithHotseatOrViceVersa();
|
||||
}
|
||||
mAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
|
@ -355,6 +378,7 @@ public class TaskbarController {
|
|||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mTaskbarView.updateHotseatItemsVisibility();
|
||||
setReplaceTaskbarWithHotseat(false);
|
||||
}
|
||||
});
|
||||
return anim.buildAnim();
|
||||
|
@ -451,6 +475,35 @@ public class TaskbarController {
|
|||
mTaskbarView.getHeight() - hotseatBounds.bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* A view was added or removed from DragLayer, check if we need to hide our hotseat copy and
|
||||
* show the real one instead.
|
||||
*/
|
||||
public void onLauncherDragLayerHierarchyChanged() {
|
||||
replaceTaskbarWithHotseatOrViceVersa();
|
||||
}
|
||||
|
||||
private void replaceTaskbarWithHotseatOrViceVersa() {
|
||||
boolean replaceTaskbarWithHotseat = AbstractFloatingView.getTopOpenViewWithType(mLauncher,
|
||||
TYPE_ALL & TYPE_REPLACE_TASKBAR_WITH_HOTSEAT) != null;
|
||||
if (!mLauncher.hasBeenResumed()) {
|
||||
replaceTaskbarWithHotseat = false;
|
||||
}
|
||||
setReplaceTaskbarWithHotseat(replaceTaskbarWithHotseat);
|
||||
}
|
||||
|
||||
private void setReplaceTaskbarWithHotseat(boolean replaceTaskbarWithHotseat) {
|
||||
Hotseat hotseat = mLauncher.getHotseat();
|
||||
if (replaceTaskbarWithHotseat) {
|
||||
alignRealHotseatWithTaskbar();
|
||||
hotseat.getReplaceTaskbarAlpha().setValue(1f);
|
||||
mTaskbarView.setHotseatViewsHidden(true);
|
||||
} else {
|
||||
hotseat.getReplaceTaskbarAlpha().setValue(0f);
|
||||
mTaskbarView.setHotseatViewsHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
private float getTaskbarScaleOnHome() {
|
||||
return 1f / mTaskbarContainerView.getTaskbarActivityContext().getTaskbarIconScale();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.android.launcher3.taskbar;
|
||||
|
||||
import android.animation.LayoutTransition;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
|
@ -63,6 +64,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
|
|||
private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
|
||||
|
||||
// Initialized in init().
|
||||
private LayoutTransition mLayoutTransition;
|
||||
private int mHotseatStartIndex;
|
||||
private int mHotseatEndIndex;
|
||||
private View mHotseatRecentsDivider;
|
||||
|
@ -76,6 +78,7 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
|
|||
private boolean mIsDraggingItem;
|
||||
// Only non-null when the corresponding Folder is open.
|
||||
private @Nullable FolderIcon mLeaveBehindFolderIcon;
|
||||
private boolean mIsHotseatHidden;
|
||||
|
||||
public TaskbarView(@NonNull Context context) {
|
||||
this(context, null);
|
||||
|
@ -107,6 +110,9 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
|
|||
}
|
||||
|
||||
protected void init(int numHotseatIcons, int numRecentIcons) {
|
||||
mLayoutTransition = new LayoutTransition();
|
||||
setLayoutTransitionsEnabled(true);
|
||||
|
||||
mHotseatStartIndex = 0;
|
||||
mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
|
||||
updateHotseatItems(new ItemInfo[numHotseatIcons]);
|
||||
|
@ -119,6 +125,10 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
|
|||
updateRecentTasks(new Task[numRecentIcons]);
|
||||
}
|
||||
|
||||
private void setLayoutTransitionsEnabled(boolean enabled) {
|
||||
setLayoutTransition(enabled ? mLayoutTransition : null);
|
||||
}
|
||||
|
||||
protected void cleanup() {
|
||||
removeAllViews();
|
||||
}
|
||||
|
@ -206,9 +216,19 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides or shows the hotseat items immediately (without layout transitions).
|
||||
*/
|
||||
protected void setHotseatViewsHidden(boolean hidden) {
|
||||
mIsHotseatHidden = hidden;
|
||||
setLayoutTransitionsEnabled(false);
|
||||
updateHotseatItemsVisibility();
|
||||
setLayoutTransitionsEnabled(true);
|
||||
}
|
||||
|
||||
private void updateHotseatItemVisibility(View hotseatView) {
|
||||
if (hotseatView.getTag() != null) {
|
||||
hotseatView.setVisibility(VISIBLE);
|
||||
hotseatView.setVisibility(mIsHotseatHidden ? INVISIBLE : VISIBLE);
|
||||
} else {
|
||||
int oldVisibility = hotseatView.getVisibility();
|
||||
int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility();
|
||||
|
|
|
@ -98,6 +98,9 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
|||
public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
|
||||
| TYPE_SNACKBAR | TYPE_WIDGET_RESIZE_FRAME | TYPE_LISTENER;
|
||||
|
||||
// When these types of floating views are open, hide the taskbar hotseat and show the real one.
|
||||
public static final int TYPE_REPLACE_TASKBAR_WITH_HOTSEAT = TYPE_FOLDER | TYPE_ACTION_POPUP;
|
||||
|
||||
public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER
|
||||
& ~TYPE_ALL_APPS_EDU;
|
||||
|
||||
|
|
|
@ -708,10 +708,11 @@ public class DeviceProfile {
|
|||
mInsets.top + availableHeightPx);
|
||||
} else {
|
||||
// Folders should only appear below the drop target bar and above the hotseat
|
||||
int hotseatTop = isTaskbarPresent ? taskbarSize : hotseatBarSizePx;
|
||||
return new Rect(mInsets.left + edgeMarginPx,
|
||||
mInsets.top + dropTargetBarSizePx + edgeMarginPx,
|
||||
mInsets.left + availableWidthPx - edgeMarginPx,
|
||||
mInsets.top + availableHeightPx - hotseatBarSizePx
|
||||
mInsets.top + availableHeightPx - hotseatTop
|
||||
- workspacePageIndicatorHeight - edgeMarginPx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.widget.FrameLayout;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
@ -37,6 +38,10 @@ import java.util.function.Consumer;
|
|||
*/
|
||||
public class Hotseat extends CellLayout implements Insettable {
|
||||
|
||||
private static final int ALPHA_INDEX_STATE = 0;
|
||||
private static final int ALPHA_INDEX_REPLACE_TASKBAR = 1;
|
||||
private static final int NUM_ALPHA_CHANNELS = 2;
|
||||
|
||||
@ViewDebug.ExportedProperty(category = "launcher")
|
||||
private boolean mHasVerticalHotseat;
|
||||
private Workspace mWorkspace;
|
||||
|
@ -44,6 +49,8 @@ public class Hotseat extends CellLayout implements Insettable {
|
|||
@Nullable
|
||||
private Consumer<Boolean> mOnVisibilityAggregatedCallback;
|
||||
|
||||
private final MultiValueAlpha mMultiValueAlpha;
|
||||
|
||||
public Hotseat(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
@ -54,6 +61,8 @@ public class Hotseat extends CellLayout implements Insettable {
|
|||
|
||||
public Hotseat(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
mMultiValueAlpha = new MultiValueAlpha(this, NUM_ALPHA_CHANNELS, MultiValueAlpha.Mode.MAX);
|
||||
mMultiValueAlpha.setUpdateVisibility(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,4 +183,12 @@ public class Hotseat extends CellLayout implements Insettable {
|
|||
public View getFirstItemMatch(Workspace.ItemOperator itemOperator) {
|
||||
return mWorkspace.getFirstMatch(new CellLayout[] { this }, itemOperator);
|
||||
}
|
||||
|
||||
public MultiValueAlpha.AlphaProperty getStateAlpha() {
|
||||
return mMultiValueAlpha.getProperty(ALPHA_INDEX_STATE);
|
||||
}
|
||||
|
||||
public MultiValueAlpha.AlphaProperty getReplaceTaskbarAlpha() {
|
||||
return mMultiValueAlpha.getProperty(ALPHA_INDEX_REPLACE_TASKBAR);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ import com.android.launcher3.graphics.SysUiScrim;
|
|||
import com.android.launcher3.graphics.WorkspaceDragScrim;
|
||||
import com.android.launcher3.states.StateAnimationConfig;
|
||||
import com.android.launcher3.util.DynamicResource;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.systemui.plugins.ResourceProvider;
|
||||
|
||||
/**
|
||||
|
@ -143,8 +144,8 @@ public class WorkspaceStateTransitionAnimation {
|
|||
}
|
||||
|
||||
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
|
||||
propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha,
|
||||
config.getInterpolator(ANIM_HOTSEAT_FADE, fadeInterpolator));
|
||||
propertySetter.setFloat(hotseat.getStateAlpha(), MultiValueAlpha.VALUE,
|
||||
hotseatIconsAlpha, config.getInterpolator(ANIM_HOTSEAT_FADE, fadeInterpolator));
|
||||
float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
|
||||
propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
|
||||
workspacePageIndicatorAlpha, fadeInterpolator);
|
||||
|
|
|
@ -42,16 +42,49 @@ public class MultiValueAlpha {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines how each alpha should factor into the final alpha.
|
||||
*/
|
||||
public enum Mode {
|
||||
BLEND(1f) {
|
||||
@Override
|
||||
public float calculateNewAlpha(float currentAlpha, float otherAlpha) {
|
||||
return currentAlpha * otherAlpha;
|
||||
}
|
||||
},
|
||||
|
||||
MAX(0f) {
|
||||
@Override
|
||||
public float calculateNewAlpha(float currentAlpha, float otherAlpha) {
|
||||
return Math.max(currentAlpha, otherAlpha);
|
||||
}
|
||||
};
|
||||
|
||||
Mode(float startAlpha) {
|
||||
mStartAlpha = startAlpha;
|
||||
}
|
||||
|
||||
protected final float mStartAlpha;
|
||||
protected abstract float calculateNewAlpha(float currentAlpha, float otherAlpha);
|
||||
}
|
||||
|
||||
private final View mView;
|
||||
private final AlphaProperty[] mMyProperties;
|
||||
private final Mode mMode;
|
||||
|
||||
private int mValidMask;
|
||||
// Whether we should change from INVISIBLE to VISIBLE and vice versa at low alpha values.
|
||||
private boolean mUpdateVisibility;
|
||||
|
||||
public MultiValueAlpha(View view, int size) {
|
||||
this(view, size, Mode.BLEND);
|
||||
}
|
||||
|
||||
public MultiValueAlpha(View view, int size, Mode mode) {
|
||||
mView = view;
|
||||
mMyProperties = new AlphaProperty[size];
|
||||
mMode = mode;
|
||||
mView.setAlpha(mMode.mStartAlpha);
|
||||
|
||||
mValidMask = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
@ -79,9 +112,9 @@ public class MultiValueAlpha {
|
|||
|
||||
private final int mMyMask;
|
||||
|
||||
private float mValue = 1;
|
||||
private float mValue = mMode.mStartAlpha;
|
||||
// Factor of all other alpha channels, only valid if mMyMask is present in mValidMask.
|
||||
private float mOthers = 1;
|
||||
private float mOthers = mMode.mStartAlpha;
|
||||
|
||||
AlphaProperty(int myMask) {
|
||||
mMyMask = myMask;
|
||||
|
@ -94,10 +127,10 @@ public class MultiValueAlpha {
|
|||
|
||||
if ((mValidMask & mMyMask) == 0) {
|
||||
// Our cache value is not correct, recompute it.
|
||||
mOthers = 1;
|
||||
mOthers = mMode.mStartAlpha;
|
||||
for (AlphaProperty prop : mMyProperties) {
|
||||
if (prop != this) {
|
||||
mOthers *= prop.mValue;
|
||||
mOthers = mMode.calculateNewAlpha(mOthers, prop.mValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +140,7 @@ public class MultiValueAlpha {
|
|||
mValidMask = mMyMask;
|
||||
mValue = value;
|
||||
|
||||
mView.setAlpha(mOthers * mValue);
|
||||
mView.setAlpha(mMode.calculateNewAlpha(mOthers, mValue));
|
||||
if (mUpdateVisibility) {
|
||||
AlphaUpdateListener.updateVisibility(mView);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue