Snap for 7678535 from 1021d42bb9 to tm-release
Change-Id: I77d93f8ef6b20bc80f2c2e76edfab2a11fac8ea7
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
-->
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.android.launcher3">
|
||||
|
||||
<!--
|
||||
@@ -176,5 +177,11 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
tools:node="remove" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -46,6 +46,23 @@
|
||||
android:layout_height="1dp"
|
||||
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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -200,6 +200,7 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
}
|
||||
|
||||
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 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
(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);
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.android.launcher3.QuickstepTransitionManager;
|
||||
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.SystemUiProxy;
|
||||
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 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -260,6 +260,13 @@ public class NavbarButtonsViewController {
|
||||
return (mState & FLAG_IME_VISIBLE) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -236,6 +236,8 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
|
||||
systemUiStateFlags, forceUpdate);
|
||||
mControllers.taskbarViewController.setImeIsVisible(
|
||||
mControllers.navbarButtonsViewController.isImeVisible());
|
||||
mControllers.taskbarViewController.setRecentsButtonDisabled(
|
||||
mControllers.navbarButtonsViewController.isRecentsDisabled());
|
||||
mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ public class TaskbarControllers {
|
||||
taskbarKeyguardController.init(navbarButtonsViewController);
|
||||
stashedHandleViewController.init(this);
|
||||
taskbarStashController.init(this);
|
||||
taskbarEduController.init(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 @@ public class TaskbarEduController {
|
||||
mTaskbarEduView.init(new TaskbarEduCallbacks());
|
||||
mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null);
|
||||
mTaskbarEduView.show();
|
||||
startAnim(createWaveAnim());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,6 +101,90 @@ public class TaskbarEduController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -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 @@ import com.android.launcher3.views.AbstractSlideInView;
|
||||
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 @@ public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext>
|
||||
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) {
|
||||
|
||||
@@ -17,6 +17,10 @@ package com.android.launcher3.taskbar;
|
||||
|
||||
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 @@ public class TaskbarUIController {
|
||||
protected void updateContentInsets(Rect outContentInsets) { }
|
||||
|
||||
protected void onStashedInAppChanged() { }
|
||||
|
||||
public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,9 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
|
||||
// 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 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
|
||||
*/
|
||||
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 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
|
||||
// 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 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
|
||||
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
|
||||
|
||||
@@ -44,6 +44,8 @@ public class TaskbarViewController {
|
||||
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 class TaskbarViewController {
|
||||
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);
|
||||
}
|
||||
@@ -100,6 +102,14 @@ public class TaskbarViewController {
|
||||
mTaskbarView.setTouchesEnabled(!isImeVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -111,6 +121,10 @@ public class TaskbarViewController {
|
||||
return mTaskbarView.getIconLayoutBounds();
|
||||
}
|
||||
|
||||
public View[] getIconViews() {
|
||||
return mTaskbarView.getIconViews();
|
||||
}
|
||||
|
||||
public AnimatedFloat getTaskbarIconScaleForStash() {
|
||||
return mTaskbarIconScaleForStash;
|
||||
}
|
||||
|
||||
@@ -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.Matrix;
|
||||
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.DeviceProfile;
|
||||
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.util.SafeCloseable;
|
||||
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 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
|
||||
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 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
|
||||
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 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
|
||||
@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 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
|
||||
}
|
||||
|
||||
@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 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,8 +43,12 @@ public class SplitScreenSelectState extends OverviewState {
|
||||
@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;
|
||||
}
|
||||
|
||||
@@ -1194,9 +1194,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
@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 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
&& 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);
|
||||
|
||||
@@ -53,7 +53,6 @@ import com.android.launcher3.statehandlers.DepthController;
|
||||
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;
|
||||
@@ -202,33 +201,6 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
|
||||
recentsView.switchToScreenshot(thumbnailData, runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -218,6 +218,11 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
|
||||
}
|
||||
}
|
||||
|
||||
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 class TaskOverlayFactory implements ResourceBasedOverride {
|
||||
public void onScreenshot() {
|
||||
endLiveTileMode(() -> saveScreenshot(mTask));
|
||||
}
|
||||
|
||||
public void onSplit() {
|
||||
endLiveTileMode(TaskOverlay.this::enterSplitSelect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,5 +345,8 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
|
||||
|
||||
/** User has indicated they want to screenshot the current task. */
|
||||
void onScreenshot();
|
||||
|
||||
/** User wants to start split screen with current app. */
|
||||
void onSplit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public interface TaskShortcutFactory {
|
||||
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);
|
||||
|
||||
@@ -32,12 +32,11 @@ import android.view.RemoteAnimationAdapter;
|
||||
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 @@ public class SplitSelectStateController {
|
||||
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 @@ public class SplitSelectStateController {
|
||||
/**
|
||||
* 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 @@ public class SplitSelectStateController {
|
||||
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 @@ public class SplitSelectStateController {
|
||||
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 @@ public class SplitSelectStateController {
|
||||
return params;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SplitPositionOption getActiveSplitPositionOption() {
|
||||
return mInitialPosition;
|
||||
public @StagePosition int getActiveSplitStagePosition() {
|
||||
return mStagePosition;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +195,7 @@ public class SplitSelectStateController {
|
||||
public void resetState() {
|
||||
mInitialTaskView = null;
|
||||
mSecondTaskView = null;
|
||||
mInitialPosition = null;
|
||||
mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
|
||||
mInitialBounds = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -145,8 +145,9 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
// 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 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
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 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
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 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
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,
|
||||
|
||||
@@ -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 @@ public class GroupedTaskView extends TaskView {
|
||||
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 @@ public class GroupedTaskView extends TaskView {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,8 +245,8 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, Laun
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
private static final int INDEX_SCROLL_ALPHA = 4;
|
||||
|
||||
private final MultiValueAlpha mMultiValueAlpha;
|
||||
private View mSplitButton;
|
||||
|
||||
@ActionsHiddenFlags
|
||||
private int mHiddenFlags;
|
||||
@@ -110,6 +111,8 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
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 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
mCallbacks.onShare();
|
||||
} else if (id == R.id.action_screenshot) {
|
||||
mCallbacks.onScreenshot();
|
||||
} else if (id == R.id.action_split) {
|
||||
mCallbacks.onSplit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +223,16 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
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. */
|
||||
|
||||
@@ -133,7 +133,7 @@ import com.android.launcher3.util.MultiValueAlpha;
|
||||
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
*/
|
||||
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
mFirstFloatingTaskView.setAlpha(1);
|
||||
mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
|
||||
mTempRect, mSplitHiddenTaskView, true /*fadeWithThumbnail*/);
|
||||
anim.addEndListener(aBoolean -> mActionsView.setSplitButtonVisible(false));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3009,8 +3015,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
* @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 abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
.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 abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
|
||||
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
mSecondSplitHiddenTaskView.setVisibility(VISIBLE);
|
||||
mSecondSplitHiddenTaskView = null;
|
||||
}
|
||||
mActionsView.setSplitButtonVisible(true);
|
||||
}
|
||||
|
||||
private void updateDeadZoneRects() {
|
||||
@@ -4038,12 +4049,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
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 abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
|
||||
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
setCurrentTask(-1);
|
||||
mRecentsAnimationController = null;
|
||||
executeSideTaskLaunchCallback();
|
||||
mRemoteTargetHandles = null;
|
||||
}
|
||||
|
||||
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
|
||||
|
||||
@@ -1514,7 +1514,7 @@ public class TaskView extends FrameLayout implements Reusable {
|
||||
|
||||
public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
|
||||
AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_TASK_MENU);
|
||||
getRecentsView().initiateSplitSelect(this, splitPositionOption);
|
||||
getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.android.quickstep.views.RecentsView;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -212,6 +213,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/197802324")
|
||||
@PortraitLandscape
|
||||
public void testAllAppsFromHome() throws Exception {
|
||||
// Test opening all apps
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -256,9 +256,27 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
|
||||
@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) {
|
||||
|
||||
@@ -2186,6 +2186,24 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
||||
}
|
||||
|
||||
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 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
||||
// No need to bind the first screen, as its always bound.
|
||||
continue;
|
||||
}
|
||||
|
||||
mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@ import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverla
|
||||
|
||||
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;
|
||||
@@ -800,6 +801,21 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
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.
|
||||
@@ -825,6 +841,22 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
@@ -32,6 +32,8 @@ public interface WorkspaceLayoutManager {
|
||||
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.
|
||||
|
||||
@@ -65,6 +65,7 @@ import com.android.launcher3.LauncherAppState;
|
||||
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 @@ public class LauncherPreviewRenderer extends ContextWrapper
|
||||
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) {
|
||||
|
||||
@@ -66,9 +66,6 @@ public class PreviewSurfaceRenderer {
|
||||
|
||||
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 @@ public class PreviewSurfaceRenderer {
|
||||
+ 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);
|
||||
|
||||
@@ -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 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
|
||||
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++) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITIO
|
||||
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.MotionEvent;
|
||||
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 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
|
||||
}
|
||||
|
||||
@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 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
|
||||
|
||||
@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 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
|
||||
|
||||
@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;
|
||||
@@ -381,6 +393,55 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
|
||||
out2.set(0, screenHeight / 2 + splitDividerSize, screenWidth, screenHeight);
|
||||
}
|
||||
|
||||
@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) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.launcher3.touch;
|
||||
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.view.accessibility.AccessibilityEvent;
|
||||
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 @@ public interface PagedOrientationHandler {
|
||||
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 @@ public interface PagedOrientationHandler {
|
||||
* @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);
|
||||
|
||||
@@ -25,6 +25,7 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MA
|
||||
|
||||
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.MotionEvent;
|
||||
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 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
|
||||
}
|
||||
|
||||
@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 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
|
||||
// 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 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
|
||||
|
||||
@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 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
|
||||
}
|
||||
|
||||
// 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 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
|
||||
|
||||
@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 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
|
||||
}
|
||||
|
||||
// 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();
|
||||
@@ -467,6 +491,92 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
|
||||
mTmpRectF.roundOut(out2);
|
||||
}
|
||||
|
||||
@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) {
|
||||
|
||||
@@ -296,7 +296,7 @@ public class IntArray implements Cloneable, Iterable<Integer> {
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
removeIndex(--mNextIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,32 +69,44 @@ public final class SplitConfigurationOptions {
|
||||
///////////////////////////////////
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.android.launcher3.tests">
|
||||
|
||||
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
|
||||
@@ -235,5 +236,11 @@
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
|
||||
<!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
tools:node="remove" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -250,6 +250,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/197802324")
|
||||
@PortraitLandscape
|
||||
public void testWidgets() throws Exception {
|
||||
// Test opening widgets.
|
||||
|
||||
Reference in New Issue
Block a user