Merge "Updates Taskbar Edu steps." into sc-v2-dev
diff --git a/quickstep/res/drawable/bg_overview_clear_all_button.xml b/quickstep/res/drawable/bg_overview_clear_all_button.xml
new file mode 100644
index 0000000..47cbd9f
--- /dev/null
+++ b/quickstep/res/drawable/bg_overview_clear_all_button.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<ripple android:color="?android:attr/colorControlHighlight"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item>
+ <shape android:shape="rectangle"
+ android:tint="?colorButtonNormal">
+ <corners android:radius="18dp" />
+ <solid android:color="?androidprv:attr/colorSurface"/>
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index 0c2a28c..acbb5b9 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -47,6 +47,23 @@
android:layout_weight="1" />
<Button
+ android:id="@+id/action_split"
+ style="@style/OverviewActionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/ic_split_screen"
+ android:text="@string/action_split"
+ android:theme="@style/ThemeControlHighlightWorkspaceColor"
+ android:visibility="gone" />
+
+ <Space
+ android:id="@+id/action_split_space"
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1"
+ android:visibility="gone" />
+
+ <Button
android:id="@+id/action_share"
style="@style/OverviewActionButton"
android:layout_width="wrap_content"
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index 1ee726e..1dea57e 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -16,7 +16,7 @@
-->
<com.android.quickstep.views.ClearAllButton
xmlns:android="http://schemas.android.com/apk/res/android"
- style="@android:style/Widget.DeviceDefault.Button.Borderless"
+ style="@style/OverviewClearAllButton"
android:id="@+id/clear_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index f1f23c4..1ec5bb8 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -166,5 +166,6 @@
<dimen name="taskbar_stashed_size">24dp</dimen>
<dimen name="taskbar_stashed_handle_width">220dp</dimen>
<dimen name="taskbar_stashed_handle_height">6dp</dimen>
- <dimen name="taskbar_edu_bg_corner_radius">28dp</dimen>
+ <dimen name="taskbar_edu_wave_anim_trans_y">25dp</dimen>
+ <dimen name="taskbar_edu_wave_anim_trans_y_return_overshoot">4dp</dimen>
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 004949a..7158287 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -191,6 +191,8 @@
<string name="action_share">Share</string>
<!-- Label for a button that causes a screen shot of the current app to be taken. [CHAR_LIMIT=40] -->
<string name="action_screenshot">Screenshot</string>
+ <!-- Label for a button that enters split screen selection mode. [CHAR_LIMIT=20] -->
+ <string name="action_split">Split</string>
<!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] -->
<string name="blocked_by_policy">This action isn\'t allowed by the app or your organization</string>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 0a69c05..b5444b5 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -135,6 +135,13 @@
<item name="android:textAllCaps">false</item>
</style>
+ <style name="OverviewClearAllButton" parent="@android:style/Widget.DeviceDefault.Button">
+ <item name="android:background">@drawable/bg_overview_clear_all_button</item>
+ <item name="android:minWidth">85dp</item>
+ <item name="android:minHeight">36dp</item>
+ <item name="android:stateListAnimator">@null</item>
+ </style>
+
<!-- Icon displayed on the taskbar -->
<style name="BaseIcon.Workspace.Taskbar" >
<item name="iconDisplay">taskbar</item>
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index b40a1d5..13baf56 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -200,6 +200,7 @@
}
int predictionIndex = 0;
+ int numViewsAnimated = 0;
ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
// make sure predicted icon removal and filling predictions don't step on each other
if (mIconRemoveAnimators != null && mIconRemoveAnimators.isRunning()) {
@@ -233,7 +234,11 @@
(WorkspaceItemInfo) mPredictedItems.get(predictionIndex++);
if (isPredictedIcon(child) && child.isEnabled()) {
PredictedAppIcon icon = (PredictedAppIcon) child;
- icon.applyFromWorkspaceItem(predictedItem);
+ boolean animateIconChange = icon.shouldAnimateIconChange(predictedItem);
+ icon.applyFromWorkspaceItem(predictedItem, animateIconChange, numViewsAnimated);
+ if (animateIconChange) {
+ numViewsAnimated++;
+ }
icon.finishBinding(mPredictionLongClickListener);
} else {
newItems.add(predictedItem);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 5777fb9..68159fa 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -26,6 +26,7 @@
import android.os.IBinder;
import android.os.SystemProperties;
import android.util.FloatProperty;
+import android.view.AttachedSurfaceControl;
import android.view.CrossWindowBlurListeners;
import android.view.SurfaceControl;
import android.view.View;
@@ -316,7 +317,12 @@
transaction.setEarlyWakeupEnd();
mInEarlyWakeUp = false;
}
- transaction.apply();
+
+ AttachedSurfaceControl rootSurfaceControl =
+ mLauncher.getRootView().getRootSurfaceControl();
+ if (rootSurfaceControl != null) {
+ rootSurfaceControl.applyTransactionOnDraw(transaction);
+ }
}
return true;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index da10bfb..acabb0d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -34,6 +34,7 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.OnboardingPrefs;
@@ -45,6 +46,9 @@
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
/**
* A data source which integrates with a Launcher instance
*/
@@ -268,6 +272,11 @@
mTaskbarOverrideBackgroundAlpha.updateValue(forceHide ? 0 : 1);
}
+ @Override
+ public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
+ return Arrays.stream(mLauncher.getAppsView().getAppsStore().getApps());
+ }
+
/**
* Starts the taskbar education flow, if the user hasn't seen it yet.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index e871c25..be9368f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -261,6 +261,13 @@
}
/**
+ * Returns true if the recents (overview) button is disabled
+ */
+ public boolean isRecentsDisabled() {
+ return (mState & FLAG_DISABLE_RECENTS) != 0;
+ }
+
+ /**
* Adds the bounds corresponding to all visible buttons to provided region
*/
public void addVisibleButtonsRegion(TaskbarDragLayer parent, Region outRegion) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 8d7a6dc..8c12567 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -236,6 +236,8 @@
systemUiStateFlags, forceUpdate);
mControllers.taskbarViewController.setImeIsVisible(
mControllers.navbarButtonsViewController.isImeVisible());
+ mControllers.taskbarViewController.setRecentsButtonDisabled(
+ mControllers.navbarButtonsViewController.isRecentsDisabled());
mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index b32a41e..1197543 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -78,6 +78,7 @@
taskbarKeyguardController.init(navbarButtonsViewController);
stashedHandleViewController.init(this);
taskbarStashController.init(this);
+ taskbarEduController.init(this);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
index ae9592d..fd5c2ea 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -15,16 +15,72 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.Keyframe;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.view.View;
+
import com.android.launcher3.R;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.uioverrides.PredictedAppIcon;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
/** Handles the Taskbar Education flow. */
public class TaskbarEduController {
+ private static final long WAVE_ANIM_DELAY = 250;
+ private static final long WAVE_ANIM_STAGGER = 50;
+ private static final long WAVE_ANIM_EACH_ICON_DURATION = 633;
+ private static final long WAVE_ANIM_SLOT_MACHINE_DURATION = 1085;
+ // The fraction of each icon's animation at which we reach the top point of the wave.
+ private static final float WAVE_ANIM_FRACTION_TOP = 0.4f;
+ // The fraction of each icon's animation at which we reach the bottom, before overshooting.
+ private static final float WAVE_ANIM_FRACTION_BOTTOM = 0.9f;
+ private static final TimeInterpolator WAVE_ANIM_TO_TOP_INTERPOLATOR = FAST_OUT_SLOW_IN;
+ private static final TimeInterpolator WAVE_ANIM_TO_BOTTOM_INTERPOLATOR = ACCEL_2;
+ private static final TimeInterpolator WAVE_ANIM_OVERSHOOT_INTERPOLATOR = DEACCEL;
+ private static final TimeInterpolator WAVE_ANIM_OVERSHOOT_RETURN_INTERPOLATOR = ACCEL_DEACCEL;
+ private static final float WAVE_ANIM_ICON_SCALE = 1.2f;
+ // How many icons to cycle through in the slot machine (+ the original icon at each end).
+ private static final int WAVE_ANIM_SLOT_MACHINE_NUM_ICONS = 3;
+
private final TaskbarActivityContext mActivity;
+ private final float mWaveAnimTranslationY;
+ private final float mWaveAnimTranslationYReturnOvershoot;
+
+ // Initialized in init.
+ TaskbarControllers mControllers;
+
private TaskbarEduView mTaskbarEduView;
+ private Animator mAnim;
public TaskbarEduController(TaskbarActivityContext activity) {
mActivity = activity;
+
+ final Resources resources = activity.getResources();
+ mWaveAnimTranslationY = resources.getDimension(R.dimen.taskbar_edu_wave_anim_trans_y);
+ mWaveAnimTranslationYReturnOvershoot = resources.getDimension(
+ R.dimen.taskbar_edu_wave_anim_trans_y_return_overshoot);
+ }
+
+ public void init(TaskbarControllers controllers) {
+ mControllers = controllers;
}
void showEdu() {
@@ -35,6 +91,7 @@
mTaskbarEduView.init(new TaskbarEduCallbacks());
mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null);
mTaskbarEduView.show();
+ startAnim(createWaveAnim());
});
}
@@ -44,6 +101,90 @@
}
}
+ /**
+ * Starts the given animation, ending the previous animation first if it's still playing.
+ */
+ private void startAnim(Animator anim) {
+ if (mAnim != null) {
+ mAnim.end();
+ }
+ mAnim = anim;
+ mAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnim = null;
+ }
+ });
+ mAnim.start();
+ }
+
+ /**
+ * Creates a staggered "wave" animation where each icon translates and scales up in succession.
+ */
+ private Animator createWaveAnim() {
+ AnimatorSet waveAnim = new AnimatorSet();
+ View[] icons = mControllers.taskbarViewController.getIconViews();
+ for (int i = 0; i < icons.length; i++) {
+ View icon = icons[i];
+ AnimatorSet iconAnim = new AnimatorSet();
+
+ Keyframe[] scaleKeyframes = new Keyframe[] {
+ Keyframe.ofFloat(0, 1f),
+ Keyframe.ofFloat(WAVE_ANIM_FRACTION_TOP, WAVE_ANIM_ICON_SCALE),
+ Keyframe.ofFloat(WAVE_ANIM_FRACTION_BOTTOM, 1f),
+ Keyframe.ofFloat(1f, 1f)
+ };
+ scaleKeyframes[1].setInterpolator(WAVE_ANIM_TO_TOP_INTERPOLATOR);
+ scaleKeyframes[2].setInterpolator(WAVE_ANIM_TO_BOTTOM_INTERPOLATOR);
+
+ Keyframe[] translationYKeyframes = new Keyframe[] {
+ Keyframe.ofFloat(0, 0f),
+ Keyframe.ofFloat(WAVE_ANIM_FRACTION_TOP, -mWaveAnimTranslationY),
+ Keyframe.ofFloat(WAVE_ANIM_FRACTION_BOTTOM, 0f),
+ // Half of the remaining fraction overshoots, then the other half returns to 0.
+ Keyframe.ofFloat(
+ WAVE_ANIM_FRACTION_BOTTOM + (1 - WAVE_ANIM_FRACTION_BOTTOM) / 2f,
+ mWaveAnimTranslationYReturnOvershoot),
+ Keyframe.ofFloat(1f, 0f)
+ };
+ translationYKeyframes[1].setInterpolator(WAVE_ANIM_TO_TOP_INTERPOLATOR);
+ translationYKeyframes[2].setInterpolator(WAVE_ANIM_TO_BOTTOM_INTERPOLATOR);
+ translationYKeyframes[3].setInterpolator(WAVE_ANIM_OVERSHOOT_INTERPOLATOR);
+ translationYKeyframes[4].setInterpolator(WAVE_ANIM_OVERSHOOT_RETURN_INTERPOLATOR);
+
+ iconAnim.play(ObjectAnimator.ofPropertyValuesHolder(icon,
+ PropertyValuesHolder.ofKeyframe(SCALE_PROPERTY, scaleKeyframes))
+ .setDuration(WAVE_ANIM_EACH_ICON_DURATION));
+ iconAnim.play(ObjectAnimator.ofPropertyValuesHolder(icon,
+ PropertyValuesHolder.ofKeyframe(View.TRANSLATION_Y, translationYKeyframes))
+ .setDuration(WAVE_ANIM_EACH_ICON_DURATION));
+
+ if (icon instanceof PredictedAppIcon) {
+ // Play slot machine animation through random icons from AllAppsList.
+ PredictedAppIcon predictedAppIcon = (PredictedAppIcon) icon;
+ ItemInfo itemInfo = (ItemInfo) icon.getTag();
+ List<BitmapInfo> iconsToAnimate = mControllers.uiController.getAppIconsForEdu()
+ .filter(appInfo -> !TextUtils.equals(appInfo.title, itemInfo.title))
+ .map(appInfo -> appInfo.bitmap)
+ .filter(bitmap -> !bitmap.isNullOrLowRes())
+ .collect(Collectors.toList());
+ // Pick n icons at random.
+ Collections.shuffle(iconsToAnimate);
+ if (iconsToAnimate.size() > WAVE_ANIM_SLOT_MACHINE_NUM_ICONS) {
+ iconsToAnimate = iconsToAnimate.subList(0, WAVE_ANIM_SLOT_MACHINE_NUM_ICONS);
+ }
+ Animator slotMachineAnim = predictedAppIcon.createSlotMachineAnim(iconsToAnimate);
+ if (slotMachineAnim != null) {
+ iconAnim.play(slotMachineAnim.setDuration(WAVE_ANIM_SLOT_MACHINE_DURATION));
+ }
+ }
+
+ iconAnim.setStartDelay(WAVE_ANIM_STAGGER * i);
+ waveAnim.play(iconAnim);
+ }
+ waveAnim.setStartDelay(WAVE_ANIM_DELAY);
+ return waveAnim;
+ }
/**
* Callbacks for {@link TaskbarEduView} to interact with its controller.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
index 9c4e844..8525427 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import android.animation.PropertyValuesHolder;
import android.content.Context;
@@ -33,6 +33,7 @@
public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext>
implements Insettable {
+ private static final int DEFAULT_OPEN_DURATION = 500;
private static final int DEFAULT_CLOSE_DURATION = 200;
private final Rect mInsets = new Rect();
@@ -129,8 +130,8 @@
mIsOpen = true;
mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
- mOpenCloseAnimator.start();
+ mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
+ mOpenCloseAnimator.setDuration(DEFAULT_OPEN_DURATION).start();
}
void snapToPage(int page) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index df88e02..c0312a0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -17,6 +17,10 @@
import android.graphics.Rect;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+
+import java.util.stream.Stream;
+
/**
* Base class for providing different taskbar UI
*/
@@ -35,4 +39,8 @@
protected void updateContentInsets(Rect outContentInsets) { }
protected void onStashedInAppChanged() { }
+
+ public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
+ return Stream.empty();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index a4a92f7..5144d9a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -90,6 +90,9 @@
// We layout the icons to be of mIconTouchSize in width and height
mItemMarginLeftRight = actualMargin - (mIconTouchSize - actualIconSize) / 2;
mItemPadding = (mIconTouchSize - actualIconSize) / 2;
+
+ // Needed to draw folder leave-behind when opening one.
+ setWillNotDraw(false);
}
protected void init(TaskbarViewController.TaskbarViewCallbacks callbacks) {
@@ -115,6 +118,7 @@
*/
protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
int nextViewIndex = 0;
+ int numViewsAnimated = 0;
for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[i];
@@ -170,8 +174,14 @@
// Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
if (hotseatView instanceof BubbleTextView
&& hotseatItemInfo instanceof WorkspaceItemInfo) {
- ((BubbleTextView) hotseatView).applyFromWorkspaceItem(
- (WorkspaceItemInfo) hotseatItemInfo);
+ BubbleTextView btv = (BubbleTextView) hotseatView;
+ WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) hotseatItemInfo;
+
+ boolean animate = btv.shouldAnimateIconChange((WorkspaceItemInfo) hotseatItemInfo);
+ btv.applyFromWorkspaceItem(workspaceInfo, animate, numViewsAnimated);
+ if (animate) {
+ numViewsAnimated++;
+ }
}
setClickAndLongClickListenersForIcon(hotseatView);
nextViewIndex++;
@@ -256,6 +266,18 @@
return mIconLayoutBounds;
}
+ /**
+ * Returns the app icons currently shown in the taskbar.
+ */
+ public View[] getIconViews() {
+ final int count = getChildCount();
+ View[] icons = new View[count];
+ for (int i = 0; i < count; i++) {
+ icons[i] = getChildAt(i);
+ }
+ return icons;
+ }
+
// FolderIconParent implemented methods.
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 1882762..40b0e18 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -44,6 +44,8 @@
public static final int ALPHA_INDEX_IME = 1;
public static final int ALPHA_INDEX_KEYGUARD = 2;
public static final int ALPHA_INDEX_STASH = 3;
+ public static final int ALPHA_INDEX_RECENTS_DISABLED = 4;
+ private static final int NUM_ALPHA_CHANNELS = 5;
private final TaskbarActivityContext mActivity;
private final TaskbarView mTaskbarView;
@@ -67,7 +69,7 @@
public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
mActivity = activity;
mTaskbarView = taskbarView;
- mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 4);
+ mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, NUM_ALPHA_CHANNELS);
mTaskbarIconAlpha.setUpdateVisibility(true);
mModelCallbacks = new TaskbarModelCallbacks(activity, mTaskbarView);
}
@@ -101,6 +103,14 @@
}
/**
+ * Should be called when the recents button is disabled, so we can hide taskbar icons as well.
+ */
+ public void setRecentsButtonDisabled(boolean isDisabled) {
+ // TODO: check TaskbarStashController#supportsStashing(), to stash instead of setting alpha.
+ mTaskbarIconAlpha.getProperty(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1);
+ }
+
+ /**
* Sets OnClickListener and OnLongClickListener for the given view.
*/
public void setClickAndLongClickListenersForIcon(View icon) {
@@ -111,6 +121,10 @@
return mTaskbarView.getIconLayoutBounds();
}
+ public View[] getIconViews() {
+ return mTaskbarView.getIconViews();
+ }
+
public AnimatedFloat getTaskbarIconScaleForStash() {
return mTaskbarIconScaleForStash;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index d839a36..ee6e8ce 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -15,6 +15,16 @@
*/
package com.android.launcher3.uioverrides;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ArgbEvaluator;
+import android.animation.Keyframe;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
@@ -23,8 +33,10 @@
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Process;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -35,6 +47,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.icons.LauncherIcons;
@@ -45,6 +59,10 @@
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.DoubleShadowBubbleTextView;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* A BubbleTextView with a ring around it's drawable
*/
@@ -53,6 +71,9 @@
private static final int RING_SHADOW_COLOR = 0x99000000;
private static final float RING_EFFECT_RATIO = 0.095f;
+ private static final long ICON_CHANGE_ANIM_DURATION = 360;
+ private static final long ICON_CHANGE_ANIM_STAGGER = 50;
+
boolean mIsDrawingDot = false;
private final DeviceProfile mDeviceProfile;
private final Paint mIconRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -67,6 +88,25 @@
private int mPlateColor;
boolean mDrawForDrag = false;
+ // Used for the "slot-machine" education animation.
+ private List<Drawable> mSlotMachineIcons;
+ private Animator mSlotMachineAnim;
+ private float mSlotMachineIconTranslationY;
+
+ private static final FloatProperty<PredictedAppIcon> SLOT_MACHINE_TRANSLATION_Y =
+ new FloatProperty<PredictedAppIcon>("slotMachineTranslationY") {
+ @Override
+ public void setValue(PredictedAppIcon predictedAppIcon, float transY) {
+ predictedAppIcon.mSlotMachineIconTranslationY = transY;
+ predictedAppIcon.invalidate();
+ }
+
+ @Override
+ public Float get(PredictedAppIcon predictedAppIcon) {
+ return predictedAppIcon.mSlotMachineIconTranslationY;
+ }
+ };
+
public PredictedAppIcon(Context context) {
this(context, null, 0);
}
@@ -88,15 +128,38 @@
@Override
public void onDraw(Canvas canvas) {
int count = canvas.save();
+ boolean isSlotMachineAnimRunning = mSlotMachineAnim != null;
if (!mIsPinned) {
drawEffect(canvas);
+ if (isSlotMachineAnimRunning) {
+ // Clip to to outside of the ring during the slot machine animation.
+ canvas.clipPath(mRingPath);
+ }
canvas.translate(getWidth() * RING_EFFECT_RATIO, getHeight() * RING_EFFECT_RATIO);
canvas.scale(1 - 2 * RING_EFFECT_RATIO, 1 - 2 * RING_EFFECT_RATIO);
}
- super.onDraw(canvas);
+ if (isSlotMachineAnimRunning) {
+ drawSlotMachineIcons(canvas);
+ } else {
+ super.onDraw(canvas);
+ }
canvas.restoreToCount(count);
}
+ private void drawSlotMachineIcons(Canvas canvas) {
+ canvas.translate((getWidth() - getIconSize()) / 2f,
+ (getHeight() - getIconSize()) / 2f + mSlotMachineIconTranslationY);
+ for (Drawable icon : mSlotMachineIcons) {
+ icon.setBounds(0, 0, getIconSize(), getIconSize());
+ icon.draw(canvas);
+ canvas.translate(0, getSlotMachineIconPlusSpacingSize());
+ }
+ }
+
+ private float getSlotMachineIconPlusSpacingSize() {
+ return getIconSize() + getOutlineOffsetY();
+ }
+
@Override
protected void drawDotIfNecessary(Canvas canvas) {
mIsDrawingDot = true;
@@ -109,9 +172,17 @@
}
@Override
- public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
- super.applyFromWorkspaceItem(info);
- mPlateColor = ColorUtils.setAlphaComponent(mDotParams.color, 200);
+ public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean animate, int staggerIndex) {
+ // Create the slot machine animation first, since it uses the current icon to start.
+ Animator slotMachineAnim = animate
+ ? createSlotMachineAnim(Collections.singletonList(info.bitmap), false)
+ : null;
+ super.applyFromWorkspaceItem(info, animate, staggerIndex);
+ int oldPlateColor = mPlateColor;
+ int newPlateColor = ColorUtils.setAlphaComponent(mDotParams.color, 200);
+ if (!animate) {
+ mPlateColor = newPlateColor;
+ }
if (mIsPinned) {
setContentDescription(info.contentDescription);
} else {
@@ -119,6 +190,76 @@
getContext().getString(R.string.hotseat_prediction_content_description,
info.contentDescription));
}
+
+ if (animate) {
+ ValueAnimator plateColorAnim = ValueAnimator.ofObject(new ArgbEvaluator(),
+ oldPlateColor, newPlateColor);
+ plateColorAnim.addUpdateListener(valueAnimator -> {
+ mPlateColor = (int) valueAnimator.getAnimatedValue();
+ invalidate();
+ });
+ AnimatorSet changeIconAnim = new AnimatorSet();
+ if (slotMachineAnim != null) {
+ changeIconAnim.play(slotMachineAnim);
+ }
+ changeIconAnim.play(plateColorAnim);
+ changeIconAnim.setStartDelay(staggerIndex * ICON_CHANGE_ANIM_STAGGER);
+ changeIconAnim.setDuration(ICON_CHANGE_ANIM_DURATION).start();
+ }
+ }
+
+ /**
+ * Returns an Animator that translates the given icons in a "slot-machine" fashion, beginning
+ * and ending with the original icon.
+ */
+ public @Nullable Animator createSlotMachineAnim(List<BitmapInfo> iconsToAnimate) {
+ return createSlotMachineAnim(iconsToAnimate, true);
+ }
+
+ /**
+ * Returns an Animator that translates the given icons in a "slot-machine" fashion, beginning
+ * with the original icon, then cycling through the given icons, optionally ending back with
+ * the original icon.
+ * @param endWithOriginalIcon Whether we should land back on the icon we started with, rather
+ * than the last item in iconsToAnimate.
+ */
+ public @Nullable Animator createSlotMachineAnim(List<BitmapInfo> iconsToAnimate,
+ boolean endWithOriginalIcon) {
+ if (mIsPinned || iconsToAnimate == null || iconsToAnimate.isEmpty()) {
+ return null;
+ }
+ if (mSlotMachineAnim != null) {
+ mSlotMachineAnim.end();
+ }
+
+ // Bookend the other animating icons with the original icon on both ends.
+ mSlotMachineIcons = new ArrayList<>(iconsToAnimate.size() + 2);
+ mSlotMachineIcons.add(getIcon());
+ iconsToAnimate.stream()
+ .map(iconInfo -> iconInfo.newThemedIcon(mContext))
+ .forEach(mSlotMachineIcons::add);
+ if (endWithOriginalIcon) {
+ mSlotMachineIcons.add(getIcon());
+ }
+
+ float finalTrans = -getSlotMachineIconPlusSpacingSize() * (mSlotMachineIcons.size() - 1);
+ Keyframe[] keyframes = new Keyframe[] {
+ Keyframe.ofFloat(0f, 0f),
+ Keyframe.ofFloat(0.82f, finalTrans - getOutlineOffsetY() / 2f), // Overshoot
+ Keyframe.ofFloat(1f, finalTrans) // Ease back into the final position
+ };
+ keyframes[1].setInterpolator(ACCEL_DEACCEL);
+ keyframes[2].setInterpolator(ACCEL_DEACCEL);
+
+ mSlotMachineAnim = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofKeyframe(SLOT_MACHINE_TRANSLATION_Y, keyframes));
+ mSlotMachineAnim.addListener(AnimatorListeners.forEndCallback(() -> {
+ mSlotMachineIcons = null;
+ mSlotMachineAnim = null;
+ mSlotMachineIconTranslationY = 0;
+ invalidate();
+ }));
+ return mSlotMachineAnim;
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index 1882a0c..9fc0450 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -43,8 +43,12 @@
@Override
public float getSplitSelectTranslation(Launcher launcher) {
RecentsView recentsView = launcher.getOverviewPanel();
- int splitPosition = recentsView.getSplitPlaceholder()
- .getActiveSplitPositionOption().mStagePosition;
+//<<<<<<< HEAD
+// int splitPosition = recentsView.getSplitPlaceholder()
+// .getActiveSplitPositionOption().stagePosition;
+//=======
+ int splitPosition = recentsView.getSplitPlaceholder().getActiveSplitStagePosition();
+//>>>>>>> f6769c8532 (Add Split button in OverviewActions)
if (!recentsView.shouldShiftThumbnailsForSplitSelect(splitPosition)) {
return 0f;
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 46d5857..6e90a3a 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1194,9 +1194,6 @@
@UiThread
private void animateToProgressInternal(float start, float end, long duration,
Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- Log.d(TestProtocol.L3_SWIPE_TO_HOME, "7");
- }
maybeUpdateRecentsAttachedState();
// If we are transitioning to launcher, then listen for the activity to be restarted while
@@ -1219,6 +1216,9 @@
}
}
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.L3_SWIPE_TO_HOME, "7, end target=" + mGestureState.getEndTarget());
+ }
if (mGestureState.getEndTarget() == HOME) {
getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
@@ -1233,6 +1233,9 @@
&& runningTaskTarget.allowEnterPip
&& runningTaskTarget.taskInfo.pictureInPictureParams != null
&& runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled();
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.L3_SWIPE_TO_HOME, "8, class=" + getClass().getSimpleName());
+ }
HomeAnimationFactory homeAnimFactory =
createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
runningTaskTarget);
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index e2ef3bc..6298bb8 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -53,7 +53,6 @@
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -203,33 +202,6 @@
}
/**
- * Sets the task size in {@param outRect} taking split screened windows into account.
- * We assume combined height of both tasks will be same as one normal task, then we'll modify
- * the task height/width based on the ratio of task screen space bounds from
- * {@param splitInfo}
- *
- * @param desiredStageBounds whether task size for top/left or bottom/right needs to be computed
- */
- public final void calculateStagedSplitTaskSize(Context context, DeviceProfile dp, Rect outRect,
- SplitConfigurationOptions.StagedSplitBounds splitInfo,
- @SplitConfigurationOptions.StagePosition int desiredStageBounds) {
- calculateTaskSize(context, dp, outRect);
-
- // TODO(b/181705607) Change for landscape vs portrait
- float totalHeight = splitInfo.mLeftTopBounds.height()
- + splitInfo.mRightBottomBounds.height()
- + splitInfo.mDividerBounds.height() / 2f;
- float topTaskPercent = splitInfo.mLeftTopBounds.height() / totalHeight;
- if (desiredStageBounds == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
- float diff = outRect.height() * (1f - topTaskPercent);
- outRect.bottom -= diff;
- } else {
- float diff = outRect.height() * topTaskPercent;
- outRect.top += diff;
- }
- }
-
- /**
* Calculates the taskView size for the provided device configuration.
*/
public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index f49c9b0..eb5c43f 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -218,6 +218,11 @@
}
}
+ private void enterSplitSelect() {
+ RecentsView overviewPanel = mThumbnailView.getTaskView().getRecentsView();
+ overviewPanel.initiateSplitSelect(mThumbnailView.getTaskView());
+ }
+
/**
* Called when the overlay is no longer used.
*/
@@ -323,6 +328,10 @@
public void onScreenshot() {
endLiveTileMode(() -> saveScreenshot(mTask));
}
+
+ public void onSplit() {
+ endLiveTileMode(TaskOverlay.this::enterSplitSelect);
+ }
}
}
@@ -336,5 +345,8 @@
/** User has indicated they want to screenshot the current task. */
void onScreenshot();
+
+ /** User wants to start split screen with current app. */
+ void onSplit();
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index a078bf3..559125e 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -100,7 +100,7 @@
private SplitPositionOption mSplitPositionOption;
public SplitSelectSystemShortcut(BaseDraggingActivity target, TaskView taskView,
SplitPositionOption option) {
- super(option.mIconResId, option.mTextResId, target, taskView.getItemInfo());
+ super(option.iconResId, option.textResId, target, taskView.getItemInfo());
mTaskView = taskView;
mSplitPositionOption = option;
setEnabled(taskView.getRecentsView().getTaskViewCount() > 1);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 20eff34..2e42392 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -146,7 +146,7 @@
private static final int SYSTEM_ACTION_ID_ALL_APPS = 14;
public static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION =
- SystemProperties.getBoolean("persist.debug.per_window_input_rotation", false);
+ SystemProperties.getBoolean("persist.debug.per_window_input_rotation", true);
private int mBackGestureNotificationCounter = -1;
@Nullable
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 16c925a..eb77ac0 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -32,12 +32,11 @@
import android.view.SurfaceControl;
import android.window.TransitionInfo;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.TaskViewUtils;
@@ -57,7 +56,7 @@
private final SystemUiProxy mSystemUiProxy;
private TaskView mInitialTaskView;
private TaskView mSecondTaskView;
- private SplitPositionOption mInitialPosition;
+ private @StagePosition int mStagePosition;
private Rect mInitialBounds;
private final Handler mHandler;
@@ -69,10 +68,10 @@
/**
* To be called after first task selected
*/
- public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption,
+ public void setInitialTaskSelect(TaskView taskView, @StagePosition int stagePosition,
Rect initialBounds) {
mInitialTaskView = taskView;
- mInitialPosition = positionOption;
+ mStagePosition = stagePosition;
mInitialBounds = initialBounds;
}
@@ -83,7 +82,11 @@
mSecondTaskView = taskView;
// Assume initial task is for top/left part of screen
- final int[] taskIds = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT
+//<<<<<<< HEAD
+// final int[] taskIds = mInitialPosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT
+//=======
+ final int[] taskIds = mStagePosition == STAGE_POSITION_TOP_OR_LEFT
+//>>>>>>> f6769c8532 (Add Split button in OverviewActions)
? new int[]{mInitialTaskView.getTask().key.id, taskView.getTask().key.id}
: new int[]{taskView.getTask().key.id, mInitialTaskView.getTask().key.id};
if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
@@ -113,7 +116,11 @@
DeviceProfile deviceProfile) {
InsettableFrameLayout.LayoutParams params =
new InsettableFrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
- boolean topLeftPosition = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT;
+//<<<<<<< HEAD
+// boolean topLeftPosition = mInitialPosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT;
+//=======
+ boolean topLeftPosition = mStagePosition == STAGE_POSITION_TOP_OR_LEFT;
+//>>>>>>> f6769c8532 (Add Split button in OverviewActions)
if (deviceProfile.isLandscape) {
params.width = (int) resources.getDimension(R.dimen.split_placeholder_size);
params.gravity = topLeftPosition ? Gravity.START : Gravity.END;
@@ -125,9 +132,8 @@
return params;
}
- @Nullable
- public SplitPositionOption getActiveSplitPositionOption() {
- return mInitialPosition;
+ public @StagePosition int getActiveSplitStagePosition() {
+ return mStagePosition;
}
/**
@@ -189,7 +195,7 @@
public void resetState() {
mInitialTaskView = null;
mSecondTaskView = null;
- mInitialPosition = null;
+ mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
mInitialBounds = null;
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 7b1c62e..9960fd3 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -145,8 +145,9 @@
// The task rect changes according to the staged split task sizes, but recents
// fullscreen scale and pivot remains the same since the task fits into the existing
// sized task space bounds
- mSizeStrategy.calculateStagedSplitTaskSize(mContext, mDp, mTaskRect, mStagedSplitBounds,
- mStagePosition);
+ mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect);
+ mOrientationState.getOrientationHandler()
+ .setSplitTaskSwipeRect(mDp, mTaskRect, mStagedSplitBounds, mStagePosition);
} else {
mTaskRect.set(fullTaskSize);
}
@@ -175,7 +176,7 @@
mStagePosition = STAGE_POSITION_UNDEFINED;
return;
}
- mStagePosition = mThumbnailPosition.equals(splitInfo.mLeftTopBounds) ?
+ mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ?
STAGE_POSITION_TOP_OR_LEFT :
STAGE_POSITION_BOTTOM_OR_RIGHT;
}
@@ -277,13 +278,9 @@
getFullScreenScale();
mThumbnailData.rotation = mOrientationState.getDisplayRotation();
- // TODO(b/195145340) handle non 50-50 split scenarios
if (mStagedSplitBounds != null) {
- if (mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
- // The preview set is for the bottom/right, inset by top/left task
- mSplitOffset.y = mStagedSplitBounds.mLeftTopBounds.height() +
- mStagedSplitBounds.mDividerBounds.height() / 2;
- }
+ mOrientationState.getOrientationHandler().setLeashSplitOffset(mSplitOffset, mDp,
+ mStagedSplitBounds, mStagePosition);
}
// mIsRecentsRtl is the inverse of TaskView RTL.
@@ -331,7 +328,7 @@
applyWindowToHomeRotation(mMatrix);
// Move lower/right split window into correct position
- mMatrix.postTranslate(0, mSplitOffset.y);
+ mMatrix.postTranslate(mSplitOffset.x, mSplitOffset.y);
// Crop rect is the inverse of thumbnail matrix
mTempRectF.set(-insets.left, -insets.top,
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index cd20f4b..1bc7c75 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,13 +1,10 @@
package com.android.quickstep.views;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
import android.content.Context;
import android.util.AttributeSet;
-import android.view.ViewGroup;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.util.CancellableTask;
@@ -48,13 +45,14 @@
mSnapshotView2 = findViewById(R.id.bottomright_snapshot);
}
- public void bind(Task primary, Task secondary, RecentsOrientedState orientedState) {
+ public void bind(Task primary, Task secondary, RecentsOrientedState orientedState,
+ SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig) {
super.bind(primary, orientedState);
mSecondaryTask = secondary;
mTaskIdContainer[1] = secondary.key.id;
mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2);
mSnapshotView2.bind(secondary);
- adjustThumbnailBoundsForSplit();
+ adjustThumbnailBoundsForSplit(splitBoundsConfig, orientedState);
}
@Override
@@ -108,30 +106,15 @@
mSnapshotView2.setOverlayEnabled(overlayEnabled);
}
- private void adjustThumbnailBoundsForSplit() {
- DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- ViewGroup.LayoutParams primaryLp = mSnapshotView.getLayoutParams();
- primaryLp.width = mSecondaryTask == null ?
- MATCH_PARENT :
- getWidth();
- int spaceAboveSnapshot = deviceProfile.overviewTaskThumbnailTopMarginPx;
- // TODO get divider height
- int dividerBar = 20;
- primaryLp.height = mSecondaryTask == null ?
- MATCH_PARENT :
- (getHeight() - spaceAboveSnapshot - dividerBar) / 2;
- mSnapshotView.setLayoutParams(primaryLp);
-
- if (mSecondaryTask == null) {
- mSnapshotView2.setVisibility(GONE);
+ private void adjustThumbnailBoundsForSplit(
+ SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig,
+ RecentsOrientedState orientedState) {
+ if (splitBoundsConfig == null) {
return;
}
- mSnapshotView2.setVisibility(VISIBLE);
- ViewGroup.LayoutParams secondaryLp = mSnapshotView2.getLayoutParams();
- secondaryLp.width = getWidth();
- secondaryLp.height = primaryLp.height;
- mSnapshotView2.setLayoutParams(secondaryLp);
- mSnapshotView2.setTranslationY(primaryLp.height + spaceAboveSnapshot + dividerBar);
+ orientedState.getOrientationHandler().setGroupedTaskViewThumbnailBounds(
+ mSnapshotView, mSnapshotView2, this, splitBoundsConfig,
+ mActivity.getDeviceProfile());
}
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 6b2d19c..ddb1fca 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -245,8 +245,8 @@
@Override
public void initiateSplitSelect(TaskView taskView,
- SplitConfigurationOptions.SplitPositionOption splitPositionOption) {
- super.initiateSplitSelect(taskView, splitPositionOption);
+ @SplitConfigurationOptions.StagePosition int stagePosition) {
+ super.initiateSplitSelect(taskView, stagePosition);
mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
}
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index a2d2179..5d1c202 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -79,6 +79,7 @@
private static final int INDEX_SCROLL_ALPHA = 4;
private final MultiValueAlpha mMultiValueAlpha;
+ private View mSplitButton;
@ActionsHiddenFlags
private int mHiddenFlags;
@@ -110,6 +111,8 @@
View share = findViewById(R.id.action_share);
share.setOnClickListener(this);
findViewById(R.id.action_screenshot).setOnClickListener(this);
+ mSplitButton = findViewById(R.id.action_split);
+ mSplitButton.setOnClickListener(this);
if (ENABLE_OVERVIEW_SHARE.get()) {
share.setVisibility(VISIBLE);
findViewById(R.id.oav_three_button_space).setVisibility(VISIBLE);
@@ -135,6 +138,8 @@
mCallbacks.onShare();
} else if (id == R.id.action_screenshot) {
mCallbacks.onScreenshot();
+ } else if (id == R.id.action_split) {
+ mCallbacks.onSplit();
}
}
@@ -218,6 +223,16 @@
mDp = dp;
updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
requestLayout();
+ setSplitButtonVisible(mDp.isTablet);
+ }
+
+ public void setSplitButtonVisible(boolean visible) {
+ if (mSplitButton == null) {
+ return;
+ }
+
+ mSplitButton.setVisibility(visible ? VISIBLE : GONE);
+ findViewById(R.id.action_split_space).setVisibility(visible ? VISIBLE : GONE);
}
/** Get the top margin associated with the action buttons in Overview. */
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 2a19ca2..646312e 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -133,7 +133,7 @@
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TranslateEdgeEffect;
import com.android.launcher3.util.ViewPool;
@@ -575,6 +575,7 @@
*/
private TaskView mSplitHiddenTaskView;
private TaskView mSecondSplitHiddenTaskView;
+ private SplitConfigurationOptions.StagedSplitBounds mSplitBoundsConfig;
/**
* Keeps track of the index of the TaskView that split screen was initialized with so we know
@@ -1314,7 +1315,8 @@
taskDataIndex--;
leftTop = tasks.get(taskDataIndex);
}
- ((GroupedTaskView) taskView).bind(leftTop, rightBottom, mOrientationState);
+ ((GroupedTaskView) taskView).bind(leftTop, rightBottom, mOrientationState,
+ mSplitBoundsConfig);
} else {
taskView.bind(task, mOrientationState);
}
@@ -2087,8 +2089,11 @@
Task.from(new TaskKey(secondaryTaskInfo), secondaryTaskInfo, false)
};
addView(taskView, mTaskViewStartIndex);
+ // When we create a placeholder task view mSplitBoundsConfig will be null, but with
+ // the actual app running we won't need to show the thumbnail until all the tasks
+ // load later anyways
((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
- mOrientationState);
+ mOrientationState, mSplitBoundsConfig);
} else {
taskView = getTaskViewFromPool(false);
addView(taskView, mTaskViewStartIndex);
@@ -2567,8 +2572,8 @@
private void createInitialSplitSelectAnimation(PendingAnimation anim) {
float placeholderHeight = getResources().getDimension(R.dimen.split_placeholder_size);
mOrientationHandler.getInitialSplitPlaceholderBounds((int) placeholderHeight,
- mActivity.getDeviceProfile(),
- mSplitSelectStateController.getActiveSplitPositionOption(), mTempRect);
+ mActivity.getDeviceProfile(),
+ mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);
RectF startingTaskRect = new RectF();
mSplitHiddenTaskView.setVisibility(INVISIBLE);
@@ -2577,6 +2582,7 @@
mFirstFloatingTaskView.setAlpha(1);
mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
mTempRect, mSplitHiddenTaskView, true /*fadeWithThumbnail*/);
+ anim.addEndListener(aBoolean -> mActionsView.setSplitButtonVisible(false));
}
/**
@@ -3009,8 +3015,7 @@
* @return {@code true} if one of the task thumbnails would intersect/overlap with the
* {@link #mFirstFloatingTaskView}
*/
- public boolean shouldShiftThumbnailsForSplitSelect(@SplitConfigurationOptions.StagePosition
- int stagePosition) {
+ public boolean shouldShiftThumbnailsForSplitSelect(@StagePosition int stagePosition) {
if (!mActivity.getDeviceProfile().isTablet) {
// Never enough space on phones
return true;
@@ -3542,12 +3547,17 @@
}
}
- public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) {
+ public void initiateSplitSelect(TaskView taskView) {
+ int defaultSplitPosition = mOrientationHandler
+ .getDefaultSplitPosition(mActivity.getDeviceProfile());
+ initiateSplitSelect(taskView, defaultSplitPosition);
+ }
+
+ public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition) {
mSplitHiddenTaskView = taskView;
Rect initialBounds = new Rect(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
taskView.getBottom());
- mSplitSelectStateController.setInitialTaskSelect(taskView,
- splitPositionOption, initialBounds);
+ mSplitSelectStateController.setInitialTaskSelect(taskView, stagePosition, initialBounds);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
finishRecentsAnimation(true, null);
@@ -3573,7 +3583,7 @@
.getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
mOrientationHandler.getFinalSplitPlaceholderBounds(halfDividerSize,
mActivity.getDeviceProfile(),
- mSplitSelectStateController.getActiveSplitPositionOption(), firstTaskEndingBounds,
+ mSplitSelectStateController.getActiveSplitStagePosition(), firstTaskEndingBounds,
secondTaskEndingBounds);
mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
@@ -3598,7 +3608,7 @@
public PendingAnimation cancelSplitSelect(boolean animate) {
SplitSelectStateController splitController = mSplitSelectStateController;
- SplitPositionOption splitOption = splitController.getActiveSplitPositionOption();
+ @StagePosition int stagePosition = splitController.getActiveSplitStagePosition();
Rect initialBounds = splitController.getInitialBounds();
splitController.resetState();
int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
@@ -3624,7 +3634,7 @@
if (child == mSplitHiddenTaskView) {
TaskView taskView = (TaskView) child;
- int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitOption,
+ int dir = mOrientationHandler.getSplitTaskViewDismissDirection(stagePosition,
mActivity.getDeviceProfile());
FloatProperty<TaskView> dismissingTaskViewTranslate;
Rect hiddenBounds = new Rect(taskView.getLeft(), taskView.getTop(),
@@ -3696,10 +3706,10 @@
return pendingAnim;
}
+ /** TODO(b/181707736) More gracefully handle exiting split selection state */
private void resetFromSplitSelectionState() {
mSplitHiddenTaskView.setTranslationY(0);
if (!showAsGrid()) {
- // TODO(b/186800707)
int pageToSnapTo = mCurrentPage;
if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
pageToSnapTo += 1;
@@ -3723,6 +3733,7 @@
mSecondSplitHiddenTaskView.setVisibility(VISIBLE);
mSecondSplitHiddenTaskView = null;
}
+ mActionsView.setSplitButtonVisible(true);
}
private void updateDeadZoneRects() {
@@ -4038,12 +4049,13 @@
mRemoteTargetHandles[1] = new RemoteTargetHandle(secondaryTvs, new TransformParams());
RemoteAnimationTargetCompat primaryTaskTarget = recentsAnimationTargets.apps[0];
RemoteAnimationTargetCompat secondaryTaskTarget = recentsAnimationTargets.apps[1];
- SplitConfigurationOptions.StagedSplitBounds
- info = new SplitConfigurationOptions.StagedSplitBounds(
+ mSplitBoundsConfig = new SplitConfigurationOptions.StagedSplitBounds(
primaryTaskTarget.screenSpaceBounds,
secondaryTaskTarget.screenSpaceBounds, dividerTarget.screenSpaceBounds);
- mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget, info);
- mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(secondaryTaskTarget, info);
+ mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget,
+ mSplitBoundsConfig);
+ mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(secondaryTaskTarget,
+ mSplitBoundsConfig);
RemoteAnimationTargets rats = new RemoteAnimationTargets(
new RemoteAnimationTargetCompat[]{primaryTaskTarget},
recentsAnimationTargets.wallpapers, recentsAnimationTargets.nonApps,
@@ -4084,6 +4096,8 @@
public void finishRecentsAnimation(boolean toRecents, boolean shouldPip,
Runnable onFinishComplete) {
+ // TODO(b/197232424#comment#10) Move this back into onRecentsAnimationComplete(). Maybe?
+ mRemoteTargetHandles = null;
if (!toRecents && ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// Reset the minimized state since we force-toggled the minimized state when entering
// overview, but never actually finished the recents animation. This is a catch all for
@@ -4131,7 +4145,6 @@
setCurrentTask(-1);
mRecentsAnimationController = null;
executeSideTaskLaunchCallback();
- mRemoteTargetHandles = null;
}
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 5986649..bfe6ca4 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1514,7 +1514,7 @@
public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_TASK_MENU);
- getRecentsView().initiateSplitSelect(this, splitPositionOption);
+ getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
}
/**
diff --git a/res/color-v31/overview_scrim_dark.xml b/res/color-v31/overview_scrim_dark.xml
index b8ed774..85ede9a 100644
--- a/res/color-v31/overview_scrim_dark.xml
+++ b/res/color-v31/overview_scrim_dark.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_800" />
+ <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
</selector>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 353e52b..02ec5e8 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -256,9 +256,27 @@
@UiThread
public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
+ applyFromWorkspaceItem(info, /* animate = */ false, /* staggerIndex = */ 0);
+ }
+
+ @UiThread
+ public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean animate, int staggerIndex) {
applyFromWorkspaceItem(info, false);
}
+ /**
+ * Returns whether the newInfo differs from the current getTag().
+ */
+ public boolean shouldAnimateIconChange(WorkspaceItemInfo newInfo) {
+ WorkspaceItemInfo oldInfo = getTag() instanceof WorkspaceItemInfo
+ ? (WorkspaceItemInfo) getTag()
+ : null;
+ boolean changedIcons = oldInfo != null && oldInfo.getTargetComponent() != null
+ && newInfo.getTargetComponent() != null
+ && !oldInfo.getTargetComponent().equals(newInfo.getTargetComponent());
+ return changedIcons && isShown();
+ }
+
@Override
public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
if (delegate instanceof LauncherAccessibilityDelegate) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 3010be1..b7d0481 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -248,7 +248,8 @@
// Tablet UI does not support emulated landscape.
isTablet = allowRotation && info.isTablet(windowBounds);
isPhone = !isTablet;
- isTwoPanels = isTablet && useTwoPanels;
+ isTwoPanels = isTablet && useTwoPanels
+ && (isLandscape || FeatureFlags.ENABLE_TWO_PANEL_HOME_IN_PORTRAIT.get());
aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4d5cc5e..6ea7b17 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2186,6 +2186,24 @@
}
private void bindAddScreens(IntArray orderedScreenIds) {
+ if (mDeviceProfile.isTwoPanels) {
+ // Some empty pages might have been removed while the phone was in a single panel
+ // mode, so we want to add those empty pages back.
+ IntSet screenIds = IntSet.wrap(orderedScreenIds);
+ for (int i = 0; i < orderedScreenIds.size(); i++) {
+ int screenId = orderedScreenIds.get(i);
+ // Don't add the page pair if the page is the last one and if the pair is on the
+ // right, because that would cause a bug when adding new pages.
+ // TODO: (b/196376162) remove this when the new screen id logic is fixed for two
+ // panel in Workspace::commitExtraEmptyScreen
+ if (i == orderedScreenIds.size() - 1 && screenId % 2 == 0) {
+ continue;
+ }
+ screenIds.add(mWorkspace.getPagePair(screenId));
+ }
+ orderedScreenIds = screenIds.getArray();
+ }
+
int count = orderedScreenIds.size();
for (int i = 0; i < count; i++) {
int screenId = orderedScreenIds.get(i);
@@ -2193,7 +2211,6 @@
// No need to bind the first screen, as its always bound.
continue;
}
-
mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 38beeea..94ec903 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -123,6 +123,7 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -333,7 +334,8 @@
int paddingBottom = grid.cellLayoutBottomPaddingPx;
int panelCount = getPanelCount();
- for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
+ int numberOfScreens = mScreenOrder.size();
+ for (int i = 0; i < numberOfScreens; i++) {
int paddingLeft = paddingLeftRight;
int paddingRight = paddingLeftRight;
if (panelCount > 1) {
@@ -348,7 +350,9 @@
paddingRight = 0;
}
}
- mWorkspaceScreens.valueAt(i).setPadding(paddingLeft, 0, paddingRight, paddingBottom);
+ // SparseArrayMap doesn't keep the order
+ mWorkspaceScreens.get(mScreenOrder.get(i))
+ .setPadding(paddingLeft, 0, paddingRight, paddingBottom);
}
}
@@ -797,6 +801,21 @@
return mScreenOrder;
}
+ /**
+ * Returns the page that is shown together with the given page when two panel is enabled.
+ * @throws IllegalStateException if called while two panel home isn't enabled.
+ */
+ public int getPagePair(int page) {
+ if (!isTwoPanelEnabled()) {
+ throw new IllegalStateException("Two panel home isn't enabled.");
+ }
+ if (page % 2 == 0) {
+ return page + 1;
+ } else {
+ return page - 1;
+ }
+ }
+
public void stripEmptyScreens() {
if (mLauncher.isWorkspaceLoading()) {
// Don't strip empty screens if the workspace is still loading.
@@ -822,6 +841,22 @@
}
}
+ // When two panel home is enabled we only remove an empty page if both visible pages are
+ // empty.
+ if (isTwoPanelEnabled()) {
+ // We go through all the pages that were marked as removable and check their page pair
+ Iterator<Integer> removeScreensIterator = removeScreens.iterator();
+ while (removeScreensIterator.hasNext()) {
+ int pageToRemove = removeScreensIterator.next();
+ int pagePair = getPagePair(pageToRemove);
+ if (!removeScreens.contains(pagePair)) {
+ // The page pair isn't empty so we want to remove the current page from the
+ // removable pages' collection
+ removeScreensIterator.remove();
+ }
+ }
+ }
+
// We enforce at least one page to add new items to. In the case that we remove the last
// such screen, we convert the last screen to the empty screen
int minScreens = 1;
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index d6302ce..44a5536 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -32,6 +32,8 @@
int EXTRA_EMPTY_SCREEN_ID = -201;
// The is the first screen. It is always present, even if its empty.
int FIRST_SCREEN_ID = 0;
+ // This is the second page. On two panel home it is always present, even if its empty.
+ int SECOND_SCREEN_ID = 1;
/**
* At bind time, we use the rank (screenId) to compute x and y for hotseat items.
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 38a957d..74c7310 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -230,6 +230,10 @@
"ENABLE_TWO_PANEL_HOME", true,
"Uses two panel on home screen. Only applicable on large screen devices.");
+ public static final BooleanFlag ENABLE_TWO_PANEL_HOME_IN_PORTRAIT = getDebugFlag(
+ "ENABLE_TWO_PANEL_HOME_IN_PORTRAIT", true,
+ "Uses two panel on home screen in portrait if ENABLE_TWO_PANEL_HOME is enabled.");
+
public static final BooleanFlag ENABLE_SCRIM_FOR_APP_LAUNCH = getDebugFlag(
"ENABLE_SCRIM_FOR_APP_LAUNCH", false,
"Enables scrim during app launch animation.");
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index a96de31..94c578e 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -65,6 +65,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
@@ -279,7 +280,7 @@
mDp.workspacePadding.top,
mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
mDp.workspacePadding.bottom);
- mWorkspaceScreens.put(PreviewSurfaceRenderer.SECOND_SCREEN_ID, rightPanel);
+ mWorkspaceScreens.put(Workspace.SECOND_SCREEN_ID, rightPanel);
}
if (Utilities.ATLEAST_S) {
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index af006d6..f4d1e61 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -66,9 +66,6 @@
private static final int FADE_IN_ANIMATION_DURATION = 200;
- // The is the second screen. It is always present in two panel, even if its empty.
- static final int SECOND_SCREEN_ID = 1;
-
private static final String KEY_HOST_TOKEN = "host_token";
private static final String KEY_VIEW_WIDTH = "width";
private static final String KEY_VIEW_HEIGHT = "height";
@@ -172,7 +169,7 @@
+ LauncherSettings.Favorites.CONTAINER_HOTSEAT;
if (deviceProfile.isTwoPanels) {
query += " or " + LauncherSettings.Favorites.SCREEN + " = "
- + SECOND_SCREEN_ID;
+ + Workspace.SECOND_SCREEN_ID;
}
loadWorkspace(new ArrayList<>(), LauncherSettings.Favorites.PREVIEW_CONTENT_URI,
query);
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index a13fa55..fea15c4 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -16,6 +16,7 @@
package com.android.launcher3.model;
import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+import static com.android.launcher3.WorkspaceLayoutManager.SECOND_SCREEN_ID;
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
@@ -299,6 +300,11 @@
IntSet screensToExclude = new IntSet();
if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
screensToExclude.add(FIRST_SCREEN_ID);
+
+ // On split display we don't want to add the new items onto the second screen.
+ if (app.getInvariantDeviceProfile().isSplitDisplay) {
+ screensToExclude.add(SECOND_SCREEN_ID);
+ }
}
for (int screen = 0; screen < screenCount; screen++) {
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 816e5dc..17a00e2 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -26,6 +26,7 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -35,13 +36,16 @@
import android.view.Surface;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.views.BaseDragLayer;
import java.util.Collections;
@@ -162,16 +166,24 @@
}
@Override
- public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+ public int getSplitTaskViewDismissDirection(@StagePosition int stagePosition,
DeviceProfile dp) {
// Don't use device profile here because we know we're in fake landscape, only split option
// available is top/left
- if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+//<<<<<<< HEAD
+// if (splitPosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+// // Top (visually left) side
+// return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
+// }
+// throw new IllegalStateException("Invalid split stage position: " +
+// splitPosition.stagePosition);
+//=======
+ if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
// Top (visually left) side
return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
}
- throw new IllegalStateException("Invalid split stage position: " +
- splitPosition.mStagePosition);
+ throw new IllegalStateException("Invalid split stage position: " + stagePosition);
+//>>>>>>> f6769c8532 (Add Split button in OverviewActions)
}
@Override
@@ -364,7 +376,7 @@
@Override
public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp,
- SplitPositionOption splitPositionOption, Rect out) {
+ @StagePosition int stagePosition, Rect out) {
// In fake land/seascape, the placeholder always needs to go to the "top" of the device,
// which is the same bounds as 0 rotation.
int width = dp.widthPx;
@@ -373,7 +385,7 @@
@Override
public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
- SplitPositionOption initialSplitOption, Rect out1, Rect out2) {
+ @StagePosition int stagePosition, Rect out1, Rect out2) {
// In fake land/seascape, the window bounds are always top and bottom half
int screenHeight = dp.heightPx;
int screenWidth = dp.widthPx;
@@ -382,6 +394,55 @@
}
@Override
+//<<<<<<< HEAD
+ public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
+ SplitConfigurationOptions.StagedSplitBounds splitInfo, int desiredStagePosition) {
+ float diff;
+ if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
+ diff = outRect.height() * (1f - splitInfo.leftTaskPercent);
+ outRect.bottom -= diff;
+ } else {
+ diff = outRect.height() * splitInfo.leftTaskPercent;
+ outRect.top += diff;
+ }
+ }
+
+ @Override
+ public void setLeashSplitOffset(Point splitOffset, DeviceProfile dp,
+ SplitConfigurationOptions.StagedSplitBounds splitInfo, int desiredStagePosition) {
+ if (desiredStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+ // The preview set is for the bottom/right, inset by top/left task
+ splitOffset.x = splitInfo.leftTopBounds.width() + splitInfo.dividerBounds.width() / 2;
+ }
+ }
+
+ @Override
+ public void setGroupedTaskViewThumbnailBounds(View mSnapshotView, View mSnapshotView2,
+ View taskParent, SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig,
+ DeviceProfile dp) {
+ int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
+ int totalThumbnailHeight = taskParent.getHeight() - spaceAboveSnapshot;
+ int totalThumbnailWidth = taskParent.getWidth();
+ int dividerBar = splitBoundsConfig.dividerBounds.width() / 2;
+ ViewGroup.LayoutParams primaryLp = mSnapshotView.getLayoutParams();
+ ViewGroup.LayoutParams secondaryLp = mSnapshotView2.getLayoutParams();
+
+ primaryLp.width = totalThumbnailWidth;
+ primaryLp.height = (int) (totalThumbnailHeight * splitBoundsConfig.leftTaskPercent);
+
+ secondaryLp.width = totalThumbnailWidth;
+ secondaryLp.height = totalThumbnailHeight - primaryLp.height - dividerBar;
+ mSnapshotView2.setTranslationY(primaryLp.height + spaceAboveSnapshot + dividerBar);
+ }
+
+//=======
+ @Override
+ public int getDefaultSplitPosition(DeviceProfile deviceProfile) {
+ throw new IllegalStateException("Default position not available in fake landscape");
+//>>>>>>> f6769c8532 (Add Split button in OverviewActions)
+ }
+
+ @Override
public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
DeviceProfile deviceProfile) {
return primary;
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index dae2dde..b34a81e 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -19,6 +19,7 @@
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -31,6 +32,7 @@
import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
@@ -78,11 +80,11 @@
FloatProperty<View> getSecondaryViewTranslate();
/**
- * @param splitPosition The position where the view to be split will go
+ * @param stagePosition The position where the view to be split will go
* @return {@link #SPLIT_TRANSLATE_*} constants to indicate which direction the
* dismissal should happen
*/
- int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition, DeviceProfile dp);
+ int getSplitTaskViewDismissDirection(@StagePosition int stagePosition, DeviceProfile dp);
int getPrimaryScroll(View view);
float getPrimaryScale(View view);
int getChildStart(View view);
@@ -120,18 +122,48 @@
* @param splitholderSize height of placeholder view in portrait, width in landscape
*/
void getInitialSplitPlaceholderBounds(int splitholderSize, DeviceProfile dp,
- SplitPositionOption splitPositionOption, Rect out);
+ @StagePosition int stagePosition, Rect out);
/**
* @param splitDividerSize height of split screen drag handle in portrait, width in landscape
- * @param initialSplitOption the split position option (top/left, bottom/right) of the first
+ * @param stagePosition the split position option (top/left, bottom/right) of the first
* task selected for entering split
* @param out1 the bounds for where the first selected app will be
* @param out2 the bounds for where the second selected app will be, complimentary to
* {@param out1} based on {@param initialSplitOption}
*/
void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
- SplitPositionOption initialSplitOption, Rect out1, Rect out2);
+ @StagePosition int stagePosition, Rect out1, Rect out2);
+
+ int getDefaultSplitPosition(DeviceProfile deviceProfile);
+
+ /**
+ * @param outRect This is expected to be the rect that has the dimensions for a non-split,
+ * fullscreen task in overview. This will directly be modified.
+ * @param desiredStagePosition Which stage position (topLeft/rightBottom) we want to resize
+ * outRect for
+ */
+ void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
+ SplitConfigurationOptions.StagedSplitBounds splitInfo,
+ @SplitConfigurationOptions.StagePosition int desiredStagePosition);
+
+ /**
+ * It's important to note that {@link #setSplitTaskSwipeRect(DeviceProfile, Rect,
+ * SplitConfigurationOptions.StagedSplitBounds, int)} above operates on the outRect based on
+ * launcher's coordinate system, meaning it will treat the outRect as portrait if home rotation
+ * is not allowed.
+ *
+ * However, here the splitOffset is from perspective of TaskViewSimulator, which is in display
+ * orientation coordinates. So, for example, for the fake landscape scenario, even though
+ * launcher is portrait, we inset the bottom/right task by an X coordinate instead of the
+ * usual Y
+ */
+ void setLeashSplitOffset(Point splitOffset, DeviceProfile dp,
+ SplitConfigurationOptions.StagedSplitBounds splitInfo,
+ @SplitConfigurationOptions.StagePosition int desiredStagePosition);
+
+ void setGroupedTaskViewThumbnailBounds(View mSnapshot1, View mSnapshot2, View taskParent,
+ SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig, DeviceProfile dp);
// Overview TaskMenuView methods
float getTaskMenuX(float x, View thumbnailView, int overScroll);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 1253589..640e6cc 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -25,6 +25,7 @@
import android.content.res.Resources;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -34,13 +35,16 @@
import android.view.Surface;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
@@ -162,9 +166,13 @@
}
@Override
- public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+ public int getSplitTaskViewDismissDirection(@StagePosition int stagePosition,
DeviceProfile dp) {
- if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+//<<<<<<< HEAD
+// if (splitPosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+//=======
+ if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+//>>>>>>> f6769c8532 (Add Split button in OverviewActions)
if (dp.isLandscape) {
// Left side
return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
@@ -172,12 +180,20 @@
// Top side
return SPLIT_TRANSLATE_SECONDARY_NEGATIVE;
}
- } else if (splitPosition.mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+//<<<<<<< HEAD
+// } else if (splitPosition.stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+ // We don't have a bottom option, so should be right
+// return SPLIT_TRANSLATE_PRIMARY_POSITIVE;
+// }
+// throw new IllegalStateException("Invalid split stage position: " +
+// splitPosition.stagePosition);
+//=======
+ } else if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
// We don't have a bottom option, so should be right
return SPLIT_TRANSLATE_PRIMARY_POSITIVE;
}
- throw new IllegalStateException("Invalid split stage position: " +
- splitPosition.mStagePosition);
+ throw new IllegalStateException("Invalid split stage position: " + stagePosition);
+//>>>>>>> f6769c8532 (Add Split button in OverviewActions)
}
@Override
@@ -413,7 +429,7 @@
@Override
public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp,
- SplitPositionOption splitPositionOption, Rect out) {
+ @StagePosition int stagePosition, Rect out) {
int width = dp.widthPx;
out.set(0, 0, width, placeholderHeight);
if (!dp.isLandscape) {
@@ -422,7 +438,11 @@
}
// Now we rotate the portrait rect depending on what side we want pinned
- boolean pinToRight = splitPositionOption.mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+//<<<<<<< HEAD
+// boolean pinToRight = splitPositionOption.stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+//=======
+ boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+//>>>>>>> f6769c8532 (Add Split button in OverviewActions)
int screenHeight = dp.heightPx;
float postRotateScale = (float) screenHeight / width;
@@ -439,7 +459,7 @@
@Override
public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
- SplitPositionOption initialSplitOption, Rect out1, Rect out2) {
+ @StagePosition int stagePosition, Rect out1, Rect out2) {
int screenHeight = dp.heightPx;
int screenWidth = dp.widthPx;
out1.set(0, 0, screenWidth, screenHeight / 2 - splitDividerSize);
@@ -450,7 +470,11 @@
}
// Now we rotate the portrait rect depending on what side we want pinned
- boolean pinToRight = initialSplitOption.mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+//<<<<<<< HEAD
+// boolean pinToRight = initialSplitOption.stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+//=======
+ boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+//>>>>>>> f6769c8532 (Add Split button in OverviewActions)
float postRotateScale = (float) screenHeight / screenWidth;
mTmpMatrix.reset();
@@ -468,6 +492,92 @@
}
@Override
+//<<<<<<< HEAD
+ public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
+ SplitConfigurationOptions.StagedSplitBounds splitInfo, int desiredStagePosition) {
+ boolean isLandscape = dp.isLandscape;
+ float diff;
+ if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
+ if (isLandscape) {
+ diff = outRect.width() * (1f - splitInfo.leftTaskPercent);
+ outRect.right -= diff;
+ } else {
+ diff = outRect.height() * (1f - splitInfo.topTaskPercent);
+ outRect.bottom -= diff;
+ }
+ } else {
+ if (isLandscape) {
+ diff = outRect.width() * splitInfo.leftTaskPercent;
+ outRect.left += diff;
+ } else {
+ diff = outRect.height() * splitInfo.topTaskPercent;
+ outRect.top += diff;
+ }
+ }
+ }
+
+ @Override
+ public void setLeashSplitOffset(Point splitOffset, DeviceProfile dp,
+ SplitConfigurationOptions.StagedSplitBounds splitInfo, int desiredStagePosition) {
+ if (desiredStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+ if (dp.isLandscape) {
+ splitOffset.x = splitInfo.leftTopBounds.width() +
+ splitInfo.dividerBounds.width() / 2;
+ } else {
+ splitOffset.y = splitInfo.leftTopBounds.height() +
+ splitInfo.dividerBounds.height() / 2;
+ }
+ }
+ }
+
+ @Override
+ public void setGroupedTaskViewThumbnailBounds(View mSnapshotView, View mSnapshotView2,
+ View taskParent, SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig,
+ DeviceProfile dp) {
+ int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
+ int totalThumbnailHeight = taskParent.getHeight() - spaceAboveSnapshot;
+ int totalThumbnailWidth = taskParent.getWidth();
+ int dividerBar = (dp.isLandscape ?
+ splitBoundsConfig.dividerBounds.width() :
+ splitBoundsConfig.dividerBounds.height())
+ / 2;
+ ViewGroup.LayoutParams primaryLp = mSnapshotView.getLayoutParams();
+ ViewGroup.LayoutParams secondaryLp = mSnapshotView2.getLayoutParams();
+
+ if (dp.isLandscape) {
+ primaryLp.height = totalThumbnailHeight;
+ primaryLp.width = (int) (totalThumbnailWidth * splitBoundsConfig.leftTaskPercent);
+
+ secondaryLp.height = totalThumbnailHeight;
+ secondaryLp.width = totalThumbnailWidth - primaryLp.width - dividerBar;
+ mSnapshotView2.setTranslationX(primaryLp.width + dividerBar);
+ mSnapshotView2.setTranslationY(spaceAboveSnapshot);
+ } else {
+ primaryLp.width = totalThumbnailWidth;
+ primaryLp.height = (int) (totalThumbnailHeight * splitBoundsConfig.topTaskPercent);
+
+ secondaryLp.width = totalThumbnailWidth;
+ secondaryLp.height = totalThumbnailHeight - primaryLp.height - dividerBar;
+ mSnapshotView2.setTranslationY(primaryLp.height + spaceAboveSnapshot + dividerBar);
+ mSnapshotView2.setTranslationX(0);
+ }
+ }
+
+//=======
+ @Override
+ public int getDefaultSplitPosition(DeviceProfile deviceProfile) {
+ if (!deviceProfile.isTablet) {
+ throw new IllegalStateException("Default position available only for large screens");
+ }
+ if (deviceProfile.isLandscape) {
+ return STAGE_POSITION_BOTTOM_OR_RIGHT;
+ } else {
+ return STAGE_POSITION_TOP_OR_LEFT;
+//>>>>>>> f6769c8532 (Add Split button in OverviewActions)
+ }
+ }
+
+ @Override
public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
DeviceProfile dp) {
if (dp.isLandscape) { // or seascape
diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java
index e7235e7..1c78795 100644
--- a/src/com/android/launcher3/util/IntArray.java
+++ b/src/com/android/launcher3/util/IntArray.java
@@ -296,7 +296,7 @@
@Override
public void remove() {
- throw new UnsupportedOperationException();
+ removeIndex(--mNextIndex);
}
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 1f1db9d..41693de 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -69,32 +69,44 @@
///////////////////////////////////
public static class SplitPositionOption {
- public final int mIconResId;
- public final int mTextResId;
+ public final int iconResId;
+ public final int textResId;
@StagePosition
- public final int mStagePosition;
+ public final int stagePosition;
@StageType
public final int mStageType;
public SplitPositionOption(int iconResId, int textResId, int stagePosition, int stageType) {
- mIconResId = iconResId;
- mTextResId = textResId;
- mStagePosition = stagePosition;
+ this.iconResId = iconResId;
+ this.textResId = textResId;
+ this.stagePosition = stagePosition;
mStageType = stageType;
}
}
public static class StagedSplitBounds {
- public final Rect mLeftTopBounds;
- public final Rect mRightBottomBounds;
- public final Rect mDividerBounds;
+ public final Rect leftTopBounds;
+ public final Rect rightBottomBounds;
+ public final Rect dividerBounds;
+ // This class is orientation-agnostic, so we compute both for later use
+ public final float topTaskPercent;
+ public final float leftTaskPercent;
public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds, Rect dividerBounds) {
- mLeftTopBounds = leftTopBounds;
- mRightBottomBounds = rightBottomBounds;
- mDividerBounds = dividerBounds;
+ this.leftTopBounds = leftTopBounds;
+ this.rightBottomBounds = rightBottomBounds;
+ this.dividerBounds = dividerBounds;
+ float totalHeight = this.leftTopBounds.height()
+ + this.rightBottomBounds.height()
+ + this.dividerBounds.height();
+ float totalWidth = this.leftTopBounds.width()
+ + this.rightBottomBounds.width()
+ + this.dividerBounds.width();
+
+ leftTaskPercent = this.leftTopBounds.width() / totalWidth;
+ topTaskPercent = this.leftTopBounds.height() / totalHeight;
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 7c73f2b..f1fe508 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -248,6 +248,8 @@
if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
if (TestHelpers.isInLauncherProcess()) {
pm.setComponentEnabledSetting(cn, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+ // b/195031154
+ SystemClock.sleep(5000);
} else {
try {
final int userId = ContextUtils.getUserId(getContext());