Merge "Force finish any pending animations if the insets or orientation change" into ub-launcher3-edmonton
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index 79d8a61..8632f8b 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/clear_all_button"
android:layout_width="wrap_content"
@@ -11,6 +10,5 @@
android:text="@string/recents_clear_all"
android:textColor="?attr/workspaceTextColor"
android:background="?android:attr/selectableItemBackground"
- launcher:layout_ignoreInsets="true"
android:textSize="14sp"
/>
\ No newline at end of file
diff --git a/quickstep/res/xml/indexable_launcher_prefs.xml b/quickstep/res/xml/indexable_launcher_prefs.xml
index 2655402..30f3100 100644
--- a/quickstep/res/xml/indexable_launcher_prefs.xml
+++ b/quickstep/res/xml/indexable_launcher_prefs.xml
@@ -20,8 +20,14 @@
android:key="pref_add_icon_to_home"
android:title="@string/auto_add_shortcuts_label"
android:summary="@string/auto_add_shortcuts_description"
- android:defaultValue="true"
- />
+ android:defaultValue="true" />
+
+ <SwitchPreference
+ android:key="pref_allowRotation"
+ android:title="@string/allow_rotation_title"
+ android:summary="@string/allow_rotation_desc"
+ android:defaultValue="@bool/allow_rotation"
+ android:persistent="true" />
<ListPreference
android:key="pref_override_icon_shape"
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index 2ebf252..e3f6543 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -101,13 +101,9 @@
}
}
- public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener,
- DeviceProfile.OnDeviceProfileChangeListener, View.OnLayoutChangeListener {
+ public static class SplitScreen extends TaskSystemShortcut {
private Handler mHandler;
- private RecentsView mRecentsView;
- private TaskView mTaskView;
- private BaseDraggingActivity mActivity;
public SplitScreen() {
super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
@@ -125,11 +121,44 @@
if (!task.isDockable) {
return null;
}
- mActivity = activity;
- mRecentsView = activity.getOverviewPanel();
- mTaskView = taskView;
+ final RecentsView recentsView = activity.getOverviewPanel();
+
final TaskThumbnailView thumbnailView = taskView.getThumbnail();
return (v -> {
+ final View.OnLayoutChangeListener onLayoutChangeListener =
+ new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int l, int t, int r, int b,
+ int oldL, int oldT, int oldR, int oldB) {
+ taskView.getRootView().removeOnLayoutChangeListener(this);
+ recentsView.removeIgnoreResetTask(taskView);
+
+ // Start animating in the side pages once launcher has been resized
+ recentsView.dismissTask(taskView, false, false);
+ }
+ };
+
+ final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
+ new DeviceProfile.OnDeviceProfileChangeListener() {
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ activity.removeOnDeviceProfileChangeListener(this);
+ if (dp.isMultiWindowMode) {
+ taskView.getRootView().addOnLayoutChangeListener(
+ onLayoutChangeListener);
+ }
+ }
+ };
+
+ final OnPreDrawListener preDrawListener = new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ taskView.getViewTreeObserver().removeOnPreDrawListener(this);
+ WindowManagerWrapper.getInstance().endProlongedAnimations();
+ return true;
+ }
+ };
+
AbstractFloatingView.closeOpenViews(activity, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
@@ -145,15 +174,15 @@
// Add a device profile change listener to kick off animating the side tasks
// once we enter multiwindow mode and relayout
- activity.addOnDeviceProfileChangeListener(this);
+ activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
final Runnable animStartedListener = () -> {
// Hide the task view and wait for the window to be resized
// TODO: Consider animating in launcher and do an in-place start activity
// afterwards
- mRecentsView.addIgnoreResetTask(mTaskView);
- mTaskView.setAlpha(0f);
- mTaskView.getViewTreeObserver().addOnPreDrawListener(SplitScreen.this);
+ recentsView.addIgnoreResetTask(taskView);
+ taskView.setAlpha(0f);
+ taskView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
};
final int[] position = new int[2];
@@ -179,31 +208,6 @@
}
});
}
-
- @Override
- public boolean onPreDraw() {
- mTaskView.getViewTreeObserver().removeOnPreDrawListener(this);
- WindowManagerWrapper.getInstance().endProlongedAnimations();
- return true;
- }
-
- @Override
- public void onDeviceProfileChanged(DeviceProfile dp) {
- mActivity.removeOnDeviceProfileChangeListener(this);
- if (dp.isMultiWindowMode) {
- mTaskView.getRootView().addOnLayoutChangeListener(this);
- }
- }
-
- @Override
- public void onLayoutChange(View v, int l, int t, int r, int b,
- int oldL, int oldT, int oldR, int oldB) {
- mTaskView.getRootView().removeOnLayoutChangeListener(this);
- mRecentsView.removeIgnoreResetTask(mTaskView);
-
- // Start animating in the side pages once launcher has been resized
- mRecentsView.dismissTask(mTaskView, false, false);
- }
}
public static class Pin extends TaskSystemShortcut {
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 5a8ce16..3d64cc8 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.support.annotation.AnyThread;
import android.util.AttributeSet;
import android.view.View;
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index 9276d238..9dd83d2 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -15,15 +15,23 @@
*/
package com.android.quickstep.util;
+import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.graphics.RectF;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.views.TaskThumbnailView;
import com.android.systemui.shared.recents.utilities.RectFEvaluator;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
/**
* Utility class to handle window clip animation
@@ -126,4 +134,73 @@
mTargetRect.offset(offsetX, offsetY);
}
}
+
+ public void fromTaskThumbnailView(TaskThumbnailView ttv) {
+ BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
+ BaseDragLayer dl = activity.getDragLayer();
+
+ int[] pos = new int[2];
+ dl.getLocationOnScreen(pos);
+ mHomeStackBounds.set(0, 0, dl.getWidth(), dl.getHeight());
+ mHomeStackBounds.offset(pos[0], pos[1]);
+
+ if (activity.isInMultiWindowModeCompat()) {
+ // TODO: Fetch multi-window target bounds from system-ui
+ DeviceProfile fullDp = activity.getDeviceProfile().getFullScreenProfile();
+ // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
+ // account for system insets
+ int taskWidth = fullDp.availableWidthPx;
+ int taskHeight = fullDp.availableHeightPx;
+ int halfDividerSize = activity.getResources()
+ .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
+
+ Rect insets = new Rect();
+ WindowManagerWrapper.getInstance().getStableInsets(insets);
+ if (fullDp.isLandscape) {
+ taskWidth = taskWidth / 2 - halfDividerSize;
+ } else {
+ taskHeight = taskHeight / 2 - halfDividerSize;
+ }
+
+ mSourceStackBounds.set(0, 0, taskWidth, taskHeight);
+ // Align the task to bottom right (probably not true for seascape).
+ mSourceStackBounds.offset(insets.left + fullDp.availableWidthPx - taskWidth,
+ insets.top + fullDp.availableHeightPx - taskHeight);
+ } else {
+ mSourceStackBounds.set(mHomeStackBounds);
+ mSourceInsets.set(activity.getDeviceProfile().getInsets());
+ }
+
+ Rect targetRect = new Rect();
+ dl.getDescendantRectRelativeToSelf(ttv, targetRect);
+ updateTargetRect(targetRect);
+
+ // Transform the clip relative to the target rect.
+ float scale = mTargetRect.width() / mSourceRect.width();
+ mSourceWindowClipInsets.left = mSourceWindowClipInsets.left * scale;
+ mSourceWindowClipInsets.top = mSourceWindowClipInsets.top * scale;
+ mSourceWindowClipInsets.right = mSourceWindowClipInsets.right * scale;
+ mSourceWindowClipInsets.bottom = mSourceWindowClipInsets.bottom * scale;
+ }
+
+ public void drawForProgress(TaskThumbnailView ttv, Canvas canvas, float progress) {
+ RectF currentRect;
+ synchronized (mTargetRect) {
+ currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTargetRect);
+ }
+
+ canvas.translate(mSourceStackBounds.left - mHomeStackBounds.left,
+ mSourceStackBounds.top - mHomeStackBounds.top);
+ mTmpMatrix.setRectToRect(mTargetRect, currentRect, ScaleToFit.FILL);
+ canvas.concat(mTmpMatrix);
+ canvas.translate(mTargetRect.left, mTargetRect.top);
+
+ float insetProgress = (1 - progress);
+ ttv.drawOnCanvas(canvas,
+ -mSourceWindowClipInsets.left * insetProgress,
+ -mSourceWindowClipInsets.top * insetProgress,
+ ttv.getMeasuredWidth() + mSourceWindowClipInsets.right * insetProgress,
+ ttv.getMeasuredHeight() + mSourceWindowClipInsets.bottom * insetProgress,
+ ttv.getCornerRadius() * progress);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java b/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
new file mode 100644
index 0000000..0fa0023
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.animation.TimeInterpolator;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.util.FloatProperty;
+import android.widget.ImageView;
+
+import com.android.launcher3.Utilities;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
+
+public class TaskViewDrawable extends Drawable {
+
+ public static FloatProperty<TaskViewDrawable> PROGRESS =
+ new FloatProperty<TaskViewDrawable>("progress") {
+ @Override
+ public void setValue(TaskViewDrawable taskViewDrawable, float v) {
+ taskViewDrawable.setProgress(v);
+ }
+
+ @Override
+ public Float get(TaskViewDrawable taskViewDrawable) {
+ return taskViewDrawable.mProgress;
+ }
+ };
+
+ private static final TimeInterpolator ICON_SIZE_INTERPOLATOR =
+ (t) -> (Math.max(t, 0.3f) - 0.3f) / 0.7f;
+
+ private final RecentsView mParent;
+ private final ImageView mIconView;
+ private final int[] mIconPos;
+
+ private final TaskThumbnailView mThumbnailView;
+
+ private final ClipAnimationHelper mClipAnimationHelper;
+
+ private float mProgress = 1;
+
+ public TaskViewDrawable(TaskView tv, RecentsView parent) {
+ mParent = parent;
+ mIconView = tv.getIconView();
+ mIconPos = new int[2];
+ Utilities.getDescendantCoordRelativeToAncestor(mIconView, parent, mIconPos, true);
+
+ mThumbnailView = tv.getThumbnail();
+ mClipAnimationHelper = new ClipAnimationHelper();
+ mClipAnimationHelper.fromTaskThumbnailView(mThumbnailView);
+ }
+
+ public void setProgress(float progress) {
+ mProgress = progress;
+ mParent.invalidate();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.save();
+ canvas.translate(mParent.getScrollX(), mParent.getScrollY());
+ mClipAnimationHelper.drawForProgress(mThumbnailView, canvas, mProgress);
+ canvas.restore();
+
+ canvas.save();
+ canvas.translate(mIconPos[0], mIconPos[1]);
+ float scale = ICON_SIZE_INTERPOLATOR.getInterpolation(mProgress);
+ canvas.scale(scale, scale, mIconView.getWidth() / 2, mIconView.getHeight() / 2);
+ mIconView.draw(canvas);
+ canvas.restore();
+ }
+
+ @Override
+ public void setAlpha(int i) { }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) { }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 280fd46..90b4bd2 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -130,4 +130,12 @@
protected void getTaskSize(DeviceProfile dp, Rect outRect) {
LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
}
+
+ @Override
+ protected void onTaskLaunched(boolean success) {
+ if (success) {
+ mActivity.getStateManager().goToState(NORMAL, false /* animate */);
+ }
+ super.onTaskLaunched(success);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 29d999d..f59ddde 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -66,10 +66,10 @@
import com.android.launcher3.util.Themes;
import com.android.quickstep.OverviewCallbacks;
import com.android.quickstep.QuickScrubController;
-import com.android.quickstep.RecentsAnimationInterpolator;
import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskUtils;
+import com.android.quickstep.util.TaskViewDrawable;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
import com.android.systemui.shared.recents.model.Task;
@@ -79,6 +79,7 @@
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
+import java.util.function.Consumer;
/**
* A list of recent tasks.
@@ -169,7 +170,7 @@
// Keeps track of task views whose visual state should not be reset
private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
- private RecentsViewContainer mContainerView;
+ private View mClearAllButton;
// Variables for empty state
private final Drawable mEmptyIcon;
@@ -311,12 +312,15 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (DEBUG_SHOW_CLEAR_ALL_BUTTON && mTouchState == TOUCH_STATE_REST && mScroller.isFinished()
- && getChildCount() != 0
- && ev.getX() > getChildAt(getChildCount() - 1).getRight() - getScrollX()) {
- // If nothing is in motion, allow events to the right of the last task to go to the
- // Clear All button.
- return false;
+ if (DEBUG_SHOW_CLEAR_ALL_BUTTON && ev.getAction() == MotionEvent.ACTION_DOWN
+ && mTouchState == TOUCH_STATE_REST && mScroller.isFinished()
+ && mClearAllButton.getVisibility() == View.VISIBLE) {
+ mClearAllButton.getHitRect(mTempRect);
+ mTempRect.offset(-getLeft(), -getTop());
+ if (mTempRect.contains((int) ev.getX(), (int) ev.getY())) {
+ // If nothing is in motion, let the Clear All button process the event.
+ return false;
+ }
}
if (ev.getAction() == MotionEvent.ACTION_UP && mShowEmptyMessage) {
@@ -522,7 +526,6 @@
mHasVisibleTaskData.clear();
}
-
protected abstract void onAllTasksRemoved();
public void reset() {
@@ -928,8 +931,8 @@
mShowEmptyMessage = isEmpty;
updateEmptyStateUi(hasSizeChanged);
invalidate();
- if (mContainerView != null) {
- mContainerView.onEmptyStateChanged(!DEBUG_SHOW_CLEAR_ALL_BUTTON || mShowEmptyMessage);
+ if (mClearAllButton != null) {
+ updateClearAllButtonVisibility();
}
}
@@ -943,10 +946,17 @@
boolean hasValidSize = getWidth() > 0 && getHeight() > 0;
if (sizeChanged && hasValidSize) {
mEmptyTextLayout = null;
+ mLastMeasureSize.set(getWidth(), getHeight());
}
- if (mShowEmptyMessage && hasValidSize && mEmptyTextLayout == null) {
- mLastMeasureSize.set(getWidth(), getHeight());
+ if (!mShowEmptyMessage) return;
+
+ // The icon needs to be centered. Need to scoll to horizontal 0 because with Clear-All
+ // space on the right, it's not guaranteed that after deleting all tasks, the horizontal
+ // scroll position will be zero.
+ scrollTo(0, 0);
+
+ if (hasValidSize && mEmptyTextLayout == null) {
int availableWidth = mLastMeasureSize.x - mEmptyMessagePadding - mEmptyMessagePadding;
mEmptyTextLayout = StaticLayout.Builder.obtain(mEmptyMessage, 0, mEmptyMessage.length(),
mEmptyMessagePaint, availableWidth)
@@ -1053,23 +1063,27 @@
return new PendingAnimation(anim);
}
- final RecentsAnimationInterpolator recentsInterpolator = tv.getRecentsInterpolator();
- ValueAnimator targetViewAnim = ValueAnimator.ofFloat(0, 1);
- targetViewAnim.addUpdateListener((animation) -> {
- float percent = animation.getAnimatedFraction();
- TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
- tv.setScaleX(tw.taskScale);
- tv.setScaleY(tw.taskScale);
- tv.setTranslationX(tw.taskX);
- tv.setTranslationY(tw.taskY);
- });
- anim.play(targetViewAnim);
+ tv.setVisibility(INVISIBLE);
+ TaskViewDrawable drawable = new TaskViewDrawable(tv, this);
+ getOverlay().add(drawable);
+
+ ObjectAnimator drawableAnim =
+ ObjectAnimator.ofFloat(drawable, TaskViewDrawable.PROGRESS, 1, 0);
+ drawableAnim.setInterpolator(LINEAR);
+
+ anim.play(drawableAnim);
anim.setDuration(duration);
+ Consumer<Boolean> onTaskLaunchFinish = (r) -> {
+ onTaskLaunched(r);
+ tv.setVisibility(VISIBLE);
+ getOverlay().remove(drawable);
+ };
+
mPendingAnimation = new PendingAnimation(anim);
mPendingAnimation.addEndListener((onEndListener) -> {
if (onEndListener.isSuccess) {
- tv.launchTask(false);
+ tv.launchTask(false, onTaskLaunchFinish, getHandler());
Task task = tv.getTask();
if (task != null) {
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
@@ -1077,13 +1091,17 @@
TaskUtils.getComponentKeyForTask(task.key));
}
} else {
- resetTaskVisuals();
+ onTaskLaunchFinish.accept(false);
}
mPendingAnimation = null;
});
return mPendingAnimation;
}
+ protected void onTaskLaunched(boolean success) {
+ resetTaskVisuals();
+ }
+
@Override
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
@@ -1119,8 +1137,13 @@
R.dimen.clear_all_container_width) - getPaddingEnd();
}
- public void setContainerView(RecentsViewContainer containerView) {
- mContainerView = containerView;
- mContainerView.onEmptyStateChanged(!DEBUG_SHOW_CLEAR_ALL_BUTTON || mShowEmptyMessage);
+ private void updateClearAllButtonVisibility() {
+ mClearAllButton.setVisibility(
+ !DEBUG_SHOW_CLEAR_ALL_BUTTON || mShowEmptyMessage ? GONE : VISIBLE);
+ }
+
+ public void setClearAllButton(View clearAllButton) {
+ mClearAllButton = clearAllButton;
+ updateClearAllButtonVisibility();
}
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index ece78c1..988b3ad 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -43,7 +43,7 @@
});
mRecentsView = (RecentsView) findViewById(R.id.overview_panel);
- mRecentsView.setContainerView(this);
+ mRecentsView.setClearAllButton(mClearAllButton);
}
@Override
@@ -56,7 +56,8 @@
(mClearAllButton.getMeasuredWidth() - getResources().getDimension(
R.dimen.clear_all_container_width)) / 2);
mClearAllButton.setTranslationY(
- mTempRect.top + (mTempRect.height() - mClearAllButton.getMeasuredHeight()) / 2);
+ mTempRect.top + (mTempRect.height() - mClearAllButton.getMeasuredHeight()) / 2
+ - mClearAllButton.getTop());
}
@Override
@@ -74,8 +75,4 @@
mRecentsView.setContentAlpha(alpha);
setVisibility(alpha > 0 ? VISIBLE : GONE);
}
-
- public void onEmptyStateChanged(boolean isEmpty) {
- mClearAllButton.setVisibility(isEmpty ? GONE : VISIBLE);
- }
}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 2f7199b..592166d 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -143,22 +143,28 @@
@Override
protected void onDraw(Canvas canvas) {
+ drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(), mCornerRadius);
+ }
+
+ public float getCornerRadius() {
+ return mCornerRadius;
+ }
+
+ public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
+ float cornerRadius) {
+ // Always draw the background since the snapshots may be translucent
+ canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mBackgroundPaint);
if (mTask == null) {
return;
}
- int width = getMeasuredWidth();
- int height = getMeasuredHeight();
-
- // Always draw the background since the snapshots may be translucent
- canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mBackgroundPaint);
if (!mTask.isLocked) {
if (mClipBottom > 0) {
canvas.save();
- canvas.clipRect(0, 0, width, mClipBottom);
- canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mPaint);
+ canvas.clipRect(x, y, width, mClipBottom);
+ canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
canvas.restore();
} else {
- canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mPaint);
+ canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 2c1318c..b32d8dd 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -131,6 +131,10 @@
return mSnapshotView;
}
+ public ImageView getIconView() {
+ return mIconView;
+ }
+
public void launchTask(boolean animate) {
launchTask(animate, null, null);
}
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bcb90e3..4fbd806 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -179,6 +179,10 @@
<string name="msg_disabled_by_admin">Disabled by your admin</string>
<!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <string name="allow_rotation_title">Allow Home screen rotation</string>
+ <!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
+ <string name="allow_rotation_desc">When phone is rotated</string>
<!-- Title for Notification dots setting. Tapping this will link to the system Notifications settings screen where the user can turn off notification dots globally. [CHAR LIMIT=50] -->
<string name="icon_badging_title">Notification dots</string>
<!-- Text to indicate that the system icon badging setting is on [CHAR LIMIT=100] -->
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 7bb19f3..3bba73a 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -34,8 +34,14 @@
android:title="@string/auto_add_shortcuts_label"
android:summary="@string/auto_add_shortcuts_description"
android:defaultValue="true"
- android:persistent="true"
- />
+ android:persistent="true" />
+
+ <SwitchPreference
+ android:key="pref_allowRotation"
+ android:title="@string/allow_rotation_title"
+ android:summary="@string/allow_rotation_desc"
+ android:defaultValue="@bool/allow_rotation"
+ android:persistent="true" />
<ListPreference
android:key="pref_override_icon_shape"
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index c9bd32b..7bd6557 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -16,6 +16,9 @@
package com.android.launcher3;
+import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
+import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
+
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
@@ -123,6 +126,16 @@
getPreferenceScreen().removePreference(iconShapeOverride);
}
}
+
+ // Setup allow rotation preference
+ Preference rotationPref = findPreference(ALLOW_ROTATION_PREFERENCE_KEY);
+ if (getResources().getBoolean(R.bool.allow_rotation)) {
+ // Launcher supports rotation by default. No need to show this setting.
+ getPreferenceScreen().removePreference(rotationPref);
+ } else {
+ // Initialize the UI once
+ rotationPref.setDefaultValue(getAllowRotationDefaultValue());
+ }
}
@Override
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 8f83648..0036bb9 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -18,28 +18,43 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.provider.Settings.System.ACCELEROMETER_ROTATION;
-import static android.provider.Settings.System.getUriFor;
+import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
+
+import static com.android.launcher3.Utilities.ATLEAST_NOUGAT;
import android.app.Activity;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.provider.Settings;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
/**
* Utility class to manage launcher rotation
*/
-public class RotationHelper extends ContentObserver {
+public class RotationHelper implements OnSharedPreferenceChangeListener {
+
+ public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
+
+ public static boolean getAllowRotationDefaultValue() {
+ if (ATLEAST_NOUGAT) {
+ // If the device was scaled, used the original dimensions to determine if rotation
+ // is allowed of not.
+ Resources res = Resources.getSystem();
+ int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
+ * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
+ return originalSmallestWidth >= 600;
+ }
+ return false;
+ }
public static final int REQUEST_NONE = 0;
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
private final Activity mActivity;
- private final ContentResolver mCr;
+ private final SharedPreferences mPrefs;
private final boolean mIgnoreAutoRotateSettings;
private boolean mAutoRotateEnabled;
@@ -60,23 +75,24 @@
private int mLastActivityFlags = -1;
public RotationHelper(Activity activity) {
- super(new Handler());
mActivity = activity;
// On large devices we do not handle auto-rotate differently.
mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation);
if (!mIgnoreAutoRotateSettings) {
- mCr = mActivity.getContentResolver();
- mCr.registerContentObserver(getUriFor(ACCELEROMETER_ROTATION), false, this);
- mAutoRotateEnabled = Settings.System.getInt(mCr, ACCELEROMETER_ROTATION, 1) == 1;
+ mPrefs = Utilities.getPrefs(mActivity);
+ mPrefs.registerOnSharedPreferenceChangeListener(this);
+ mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
+ getAllowRotationDefaultValue());
} else {
- mCr = null;
+ mPrefs = null;
}
}
@Override
- public void onChange(boolean selfChange) {
- mAutoRotateEnabled = Settings.System.getInt(mCr, ACCELEROMETER_ROTATION, 1) == 1;
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+ mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
+ getAllowRotationDefaultValue());
notifyChange();
}
@@ -104,8 +120,8 @@
public void destroy() {
if (!mDestroyed) {
mDestroyed = true;
- if (mCr != null) {
- mCr.unregisterContentObserver(this);
+ if (mPrefs != null) {
+ mPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
}
}
@@ -121,19 +137,17 @@
SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
} else if (mCurrentStateRequest == REQUEST_LOCK) {
activityFlags = SCREEN_ORIENTATION_LOCKED;
- } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE) {
+ } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE
+ || mAutoRotateEnabled) {
activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
- } else if (mAutoRotateEnabled) {
- // If auto rotation is on, lock to device orientation
- activityFlags = SCREEN_ORIENTATION_NOSENSOR;
} else {
// If auto rotation is off, allow rotation on the activity, in case the user is using
// forced rotation.
- activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
+ activityFlags = SCREEN_ORIENTATION_NOSENSOR;
}
if (activityFlags != mLastActivityFlags) {
mLastActivityFlags = activityFlags;
- mActivity.setRequestedOrientation(mLastActivityFlags);
+ mActivity.setRequestedOrientation(activityFlags);
}
}
}