summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2016-10-10 21:00:09 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2016-10-10 21:00:10 +0000
commit631ffbda64b92b843f18af6756b52b1ac4c22461 (patch)
tree6c9c56068f427cfcf5851e1bca0d8714ab554be6 /src
parent51e15402faabf708fa707cefcbb28d7883fed1d0 (diff)
parent740ac7f00e0b847b8e392800f7948d93493e11d6 (diff)
downloadandroid_packages_apps_Trebuchet-631ffbda64b92b843f18af6756b52b1ac4c22461.tar.gz
android_packages_apps_Trebuchet-631ffbda64b92b843f18af6756b52b1ac4c22461.tar.bz2
android_packages_apps_Trebuchet-631ffbda64b92b843f18af6756b52b1ac4c22461.zip
Merge "Refactoring floating view opening/closing logic" into ub-launcher3-master
Diffstat (limited to 'src')
-rw-r--r--src/com/android/launcher3/AbstractFloatingView.java124
-rw-r--r--src/com/android/launcher3/ExtendedEditText.java8
-rw-r--r--src/com/android/launcher3/FolderInfo.java5
-rw-r--r--src/com/android/launcher3/Launcher.java290
-rw-r--r--src/com/android/launcher3/PinchToOverviewListener.java2
-rw-r--r--src/com/android/launcher3/Workspace.java24
-rw-r--r--src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java12
-rw-r--r--src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java3
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java3
-rw-r--r--src/com/android/launcher3/dragndrop/DragLayer.java66
-rw-r--r--src/com/android/launcher3/folder/Folder.java259
-rw-r--r--src/com/android/launcher3/folder/FolderIcon.java88
-rw-r--r--src/com/android/launcher3/folder/PreviewImageView.java98
-rw-r--r--src/com/android/launcher3/keyboard/CustomActionsPopup.java2
-rw-r--r--src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java42
15 files changed, 536 insertions, 490 deletions
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
new file mode 100644
index 000000000..65da00211
--- /dev/null
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.support.annotation.IntDef;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.launcher3.dragndrop.DragLayer;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base class for a View which shows a floating UI on top of the launcher UI.
+ */
+public abstract class AbstractFloatingView extends LinearLayout {
+
+ @IntDef(flag = true, value = {TYPE_FOLDER, TYPE_DEEPSHORTCUT_CONTAINER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FloatingViewType {}
+ public static final int TYPE_FOLDER = 1 << 0;
+ public static final int TYPE_DEEPSHORTCUT_CONTAINER = 1 << 1;
+
+ protected boolean mIsOpen;
+
+ public AbstractFloatingView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AbstractFloatingView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public final void close(boolean animate) {
+ animate &= !Utilities.isPowerSaverOn(getContext());
+ handleClose(animate);
+ Launcher.getLauncher(getContext()).getUserEventDispatcher().resetElapsedContainerMillis();
+ }
+
+ protected abstract void handleClose(boolean animate);
+
+ /**
+ * If the view is current handling keyboard, return the active target, null otherwise
+ */
+ public ExtendedEditText getActiveTextView() {
+ return null;
+ }
+
+
+ /**
+ * Any additional view (outside of this container) where touch should be allowed while this
+ * view is visible.
+ */
+ public View getExtendedTouchView() {
+ return null;
+ }
+
+ public final boolean isOpen() {
+ return mIsOpen;
+ }
+
+ protected abstract boolean isOfType(@FloatingViewType int type);
+
+ protected static <T extends AbstractFloatingView> T getOpenView(
+ Launcher launcher, @FloatingViewType int type) {
+ DragLayer dragLayer = launcher.getDragLayer();
+ // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
+ // and will be one of the last views.
+ for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
+ View child = dragLayer.getChildAt(i);
+ if (child instanceof AbstractFloatingView) {
+ AbstractFloatingView view = (AbstractFloatingView) child;
+ if (view.isOfType(type) && view.isOpen()) {
+ return (T) view;
+ }
+ }
+ }
+ return null;
+ }
+
+ protected static void closeOpenContainer(Launcher launcher, @FloatingViewType int type) {
+ AbstractFloatingView view = getOpenView(launcher, type);
+ if (view != null) {
+ view.close(true);
+ }
+ }
+
+ public static void closeAllOpenViews(Launcher launcher, boolean animate) {
+ DragLayer dragLayer = launcher.getDragLayer();
+ // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
+ // and will be one of the last views.
+ for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
+ View child = dragLayer.getChildAt(i);
+ if (child instanceof AbstractFloatingView) {
+ ((AbstractFloatingView) child).close(animate);
+ }
+ }
+ }
+
+ public static void closeAllOpenViews(Launcher launcher) {
+ closeAllOpenViews(launcher, true);
+ }
+
+ public static AbstractFloatingView getTopOpenView(Launcher launcher) {
+ return getOpenView(launcher, TYPE_FOLDER | TYPE_DEEPSHORTCUT_CONTAINER);
+ }
+}
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index c06f727a5..d05673ccc 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -99,4 +99,12 @@ public class ExtendedEditText extends EditText {
((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT);
}
+
+ public void dispatchBackKey() {
+ ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
+ .hideSoftInputFromWindow(getWindowToken(), 0);
+ if (mBackKeyListener != null) {
+ mBackKeyListener.onBackKey();
+ }
+ }
}
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index c0a8caaa3..8b70d1c70 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -45,11 +45,6 @@ public class FolderInfo extends ItemInfo {
*/
public static final int FLAG_MULTI_PAGE_ANIMATION = 0x00000004;
- /**
- * Whether this folder has been opened
- */
- public boolean opened;
-
public int options;
/**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b1700ccbb..43a250b3a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,9 +18,7 @@ package com.android.launcher3;
import android.Manifest;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@@ -47,8 +45,6 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
@@ -79,10 +75,8 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.OvershootInterpolator;
import android.view.inputmethod.InputMethodManager;
-import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@@ -295,13 +289,6 @@ public class Launcher extends Activity
// it from the context.
private SharedPreferences mSharedPrefs;
- // Holds the page that we need to animate to, and the icon views that we need to animate up
- // when we scroll to that page on resume.
- @Thunk ImageView mFolderIconImageView;
- private Bitmap mFolderIconBitmap;
- private Canvas mFolderIconCanvas;
- private Rect mRectForFolderAnimation = new Rect();
-
private DeviceProfile mDeviceProfile;
private boolean mMoveToDefaultScreenFromNewIntent;
@@ -1231,11 +1218,8 @@ public class Launcher extends Activity
if (keyCode == KeyEvent.KEYCODE_MENU) {
// Ignore the menu key if we are currently dragging or are on the custom content screen
if (!isOnCustomContent() && !mDragController.isDragging()) {
- // Close any open folders
- closeFolder();
-
- // Close any shortcuts containers
- closeShortcutsContainer();
+ // Close any open floating view
+ AbstractFloatingView.closeAllOpenViews(this);
// Stop resizing any widgets
mWorkspace.exitWidgetResizeMode();
@@ -1705,7 +1689,7 @@ public class Launcher extends Activity
// Check this condition before handling isActionMain, as this will get reset.
boolean shouldMoveToDefaultScreen = alreadyOnHome &&
- mState == State.WORKSPACE && getTopFloatingView() == null;
+ mState == State.WORKSPACE && AbstractFloatingView.getTopOpenView(this) == null;
boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
if (isActionMain) {
@@ -1719,8 +1703,7 @@ public class Launcher extends Activity
// In all these cases, only animate if we're already on home
mWorkspace.exitWidgetResizeMode();
- closeFolder(alreadyOnHome);
- closeShortcutsContainer(alreadyOnHome);
+ AbstractFloatingView.closeAllOpenViews(this, alreadyOnHome);
exitSpringLoadedDragMode();
// If we are already on home, then just animate back to the workspace,
@@ -1803,11 +1786,9 @@ public class Launcher extends Activity
super.onSaveInstanceState(outState);
outState.putInt(RUNTIME_STATE, mState.ordinal());
- // We close any open folder since it will not be re-opened, and we need to make sure
- // this state is reflected.
- // TODO: Move folderInfo.isOpened out of the model and make it a UI state.
- closeFolder(false);
- closeShortcutsContainer(false);
+ // We close any open folders and shortcut containers since they will not be re-opened,
+ // and we need to make sure this state is reflected.
+ AbstractFloatingView.closeAllOpenViews(this, false);
if (mPendingRequestArgs != null) {
outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
@@ -2036,7 +2017,7 @@ public class Launcher extends Activity
protected void moveToCustomContentScreen(boolean animate) {
// Close any folders that may be open.
- closeFolder();
+ AbstractFloatingView.closeAllOpenViews(this, animate);
mWorkspace.moveToCustomContentScreen(animate);
}
@@ -2227,21 +2208,19 @@ public class Launcher extends Activity
return;
}
- if (getOpenShortcutsContainer() != null) {
- closeShortcutsContainer();
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
+ if (topView != null) {
+ if (topView.getActiveTextView() != null) {
+ topView.getActiveTextView().dispatchBackKey();
+ } else {
+ topView.close(true);
+ }
} else if (isAppsViewVisible()) {
showWorkspace(true);
} else if (isWidgetsViewVisible()) {
showOverviewMode(true);
} else if (mWorkspace.isInOverviewMode()) {
showWorkspace(true);
- } else if (mWorkspace.getOpenFolder() != null) {
- Folder openFolder = mWorkspace.getOpenFolder();
- if (openFolder.isEditingName()) {
- openFolder.dismissEditingName();
- } else {
- closeFolder();
- }
} else {
mWorkspace.exitWidgetResizeMode();
@@ -2491,10 +2470,10 @@ public class Launcher extends Activity
throw new IllegalArgumentException("Input must be a FolderIcon");
}
- FolderIcon folderIcon = (FolderIcon) v;
- if (!folderIcon.getFolderInfo().opened && !folderIcon.getFolder().isDestroyed()) {
+ Folder folder = ((FolderIcon) v).getFolder();
+ if (!folder.isOpen() && !folder.isDestroyed()) {
// Open the requested folder
- openFolder(folderIcon);
+ folder.animateOpen();
}
}
@@ -2735,233 +2714,6 @@ public class Launcher extends Activity
return false;
}
- /**
- * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
- * in the DragLayer in the exact absolute location of the original FolderIcon.
- */
- private void copyFolderIconToImage(FolderIcon fi) {
- final int width = fi.getMeasuredWidth();
- final int height = fi.getMeasuredHeight();
-
- // Lazy load ImageView, Bitmap and Canvas
- if (mFolderIconImageView == null) {
- mFolderIconImageView = new ImageView(this);
- }
- if (mFolderIconBitmap == null || mFolderIconBitmap.getWidth() != width ||
- mFolderIconBitmap.getHeight() != height) {
- mFolderIconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mFolderIconCanvas = new Canvas(mFolderIconBitmap);
- }
-
- DragLayer.LayoutParams lp;
- if (mFolderIconImageView.getLayoutParams() instanceof DragLayer.LayoutParams) {
- lp = (DragLayer.LayoutParams) mFolderIconImageView.getLayoutParams();
- } else {
- lp = new DragLayer.LayoutParams(width, height);
- }
-
- // The layout from which the folder is being opened may be scaled, adjust the starting
- // view size by this scale factor.
- float scale = mDragLayer.getDescendantRectRelativeToSelf(fi, mRectForFolderAnimation);
- lp.customPosition = true;
- lp.x = mRectForFolderAnimation.left;
- lp.y = mRectForFolderAnimation.top;
- lp.width = (int) (scale * width);
- lp.height = (int) (scale * height);
-
- mFolderIconCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
- fi.draw(mFolderIconCanvas);
- mFolderIconImageView.setImageBitmap(mFolderIconBitmap);
- if (fi.getFolder() != null) {
- mFolderIconImageView.setPivotX(fi.getFolder().getPivotXForIconAnimation());
- mFolderIconImageView.setPivotY(fi.getFolder().getPivotYForIconAnimation());
- }
- // Just in case this image view is still in the drag layer from a previous animation,
- // we remove it and re-add it.
- if (mDragLayer.indexOfChild(mFolderIconImageView) != -1) {
- mDragLayer.removeView(mFolderIconImageView);
- }
- mDragLayer.addView(mFolderIconImageView, lp);
- if (fi.getFolder() != null) {
- fi.getFolder().bringToFront();
- }
- }
-
- private void growAndFadeOutFolderIcon(FolderIcon fi) {
- if (fi == null) return;
- FolderInfo info = (FolderInfo) fi.getTag();
- if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- CellLayout cl = (CellLayout) fi.getParent().getParent();
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) fi.getLayoutParams();
- cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
- }
-
- // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
- copyFolderIconToImage(fi);
- fi.setVisibility(View.INVISIBLE);
-
- ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(
- mFolderIconImageView, 0, 1.5f, 1.5f);
- if (Utilities.ATLEAST_LOLLIPOP) {
- oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
- }
- oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
- oa.start();
- }
-
- private void shrinkAndFadeInFolderIcon(final FolderIcon fi, boolean animate) {
- if (fi == null) return;
- final CellLayout cl = (CellLayout) fi.getParent().getParent();
-
- // We remove and re-draw the FolderIcon in-case it has changed
- mDragLayer.removeView(mFolderIconImageView);
- copyFolderIconToImage(fi);
-
- if (cl != null) {
- cl.clearFolderLeaveBehind();
- }
-
- ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(mFolderIconImageView, 1, 1, 1);
- oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
- oa.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (cl != null) {
- // Remove the ImageView copy of the FolderIcon and make the original visible.
- mDragLayer.removeView(mFolderIconImageView);
- fi.setVisibility(View.VISIBLE);
- }
- }
- });
- oa.start();
- if (!animate) {
- oa.end();
- }
- }
-
- /**
- * Opens the user folder described by the specified tag. The opening of the folder
- * is animated relative to the specified View. If the View is null, no animation
- * is played.
- *
- * @param folderIcon The FolderIcon describing the folder to open.
- */
- public void openFolder(FolderIcon folderIcon) {
-
- Folder folder = folderIcon.getFolder();
- Folder openFolder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
- if (openFolder != null && openFolder != folder) {
- // Close any open folder before opening a folder.
- closeFolder();
- }
-
- FolderInfo info = folder.mInfo;
-
- info.opened = true;
-
- // While the folder is open, the position of the icon cannot change.
- ((CellLayout.LayoutParams) folderIcon.getLayoutParams()).canReorder = false;
-
- // Just verify that the folder hasn't already been added to the DragLayer.
- // There was a one-off crash where the folder had a parent already.
- if (folder.getParent() == null) {
- mDragLayer.addView(folder);
- mDragController.addDropTarget(folder);
- } else {
- Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
- folder.getParent() + ").");
- }
- folder.animateOpen();
-
- growAndFadeOutFolderIcon(folderIcon);
-
- // Notify the accessibility manager that this folder "window" has appeared and occluded
- // the workspace items
- folder.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-
- getUserEventDispatcher().resetElapsedContainerMillis();
- }
-
- public void closeFolder() {
- closeFolder(true);
- }
-
- public void closeFolder(boolean animate) {
- Folder folder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
- if (folder != null) {
- if (folder.isEditingName()) {
- folder.dismissEditingName();
- }
- closeFolder(folder, animate);
- }
- }
-
- public void closeFolder(Folder folder, boolean animate) {
- animate &= !Utilities.isPowerSaverOn(this);
-
- folder.getInfo().opened = false;
-
- ViewGroup parent = (ViewGroup) folder.getParent().getParent();
- if (parent != null) {
- FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo);
- shrinkAndFadeInFolderIcon(fi, animate);
- if (fi != null) {
- ((CellLayout.LayoutParams) fi.getLayoutParams()).canReorder = true;
- }
- }
- if (animate) {
- folder.animateClosed();
- } else {
- folder.close(false);
- }
-
- // Notify the accessibility manager that this folder "window" has disappeared and no
- // longer occludes the workspace items
- getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-
- getUserEventDispatcher().resetElapsedContainerMillis();
- }
-
- public void closeShortcutsContainer() {
- closeShortcutsContainer(true);
- }
-
- public void closeShortcutsContainer(boolean animate) {
- DeepShortcutsContainer deepShortcutsContainer = getOpenShortcutsContainer();
- if (deepShortcutsContainer != null) {
- if (animate) {
- deepShortcutsContainer.animateClose();
- } else {
- deepShortcutsContainer.close();
- }
- }
- }
-
- public View getTopFloatingView() {
- View topView = getOpenShortcutsContainer();
- if (topView == null) {
- topView = getWorkspace().getOpenFolder();
- }
- return topView;
- }
-
- /**
- * @return The open shortcuts container, or null if there is none
- */
- public DeepShortcutsContainer getOpenShortcutsContainer() {
- // Iterate in reverse order. Shortcuts container is added later to the dragLayer,
- // and will be one of the last views.
- for (int i = mDragLayer.getChildCount() - 1; i >= 0; i--) {
- View child = mDragLayer.getChildAt(i);
- if (child instanceof DeepShortcutsContainer
- && ((DeepShortcutsContainer) child).isOpen()) {
- return (DeepShortcutsContainer) child;
- }
- }
- return null;
- }
-
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
mLastDispatchTouchEventX = ev.getX();
@@ -3220,9 +2972,7 @@ public class Launcher extends Activity
// Change the state *after* we've called all the transition code
mState = toState;
-
- closeFolder();
- closeShortcutsContainer();
+ AbstractFloatingView.closeAllOpenViews(this);
// Send an accessibility event to announce the context change
getWindow().getDecorView()
@@ -4379,7 +4129,7 @@ public class Launcher extends Activity
&& mAccessibilityDelegate.performAction(focusedView,
(ItemInfo) focusedView.getTag(),
LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
- getOpenShortcutsContainer().requestFocus();
+ DeepShortcutsContainer.getOpen(this).requestFocus();
return true;
}
break;
diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java
index bc5ac2456..66209bfa1 100644
--- a/src/com/android/launcher3/PinchToOverviewListener.java
+++ b/src/com/android/launcher3/PinchToOverviewListener.java
@@ -102,7 +102,7 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG
// once the state switching animation is complete.
return false;
}
- if (mLauncher.getTopFloatingView() != null) {
+ if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
// Don't listen for the pinch gesture if a floating view is open.
return false;
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index e583be483..f5297358b 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -553,24 +553,6 @@ public class Workspace extends PagedView
cl.getBackgroundAlpha() > 0);
}
- /**
- * @return The open folder on the current screen, or null if there is none
- */
- public Folder getOpenFolder() {
- DragLayer dragLayer = mLauncher.getDragLayer();
- // Iterate in reverse order. Folder is added later to the dragLayer,
- // and will be one of the last views.
- for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
- View child = dragLayer.getChildAt(i);
- if (child instanceof Folder) {
- Folder folder = (Folder) child;
- if (folder.getInfo().opened)
- return folder;
- }
- }
- return null;
- }
-
boolean isTouchActive() {
return mTouchState != TOUCH_STATE_REST;
}
@@ -3811,7 +3793,7 @@ public class Workspace extends PagedView
if (!workspaceInModalState() && !mIsSwitchingState) {
super.scrollLeft();
}
- Folder openFolder = getOpenFolder();
+ Folder openFolder = Folder.getOpen(mLauncher);
if (openFolder != null) {
openFolder.completeDragExit();
}
@@ -3822,7 +3804,7 @@ public class Workspace extends PagedView
if (!workspaceInModalState() && !mIsSwitchingState) {
super.scrollRight();
}
- Folder openFolder = getOpenFolder();
+ Folder openFolder = Folder.getOpen(mLauncher);
if (openFolder != null) {
openFolder.completeDragExit();
}
@@ -3841,7 +3823,7 @@ public class Workspace extends PagedView
}
boolean result = false;
- if (!workspaceInModalState() && !mIsSwitchingState && getOpenFolder() == null) {
+ if (!workspaceInModalState() && !mIsSwitchingState && Folder.getOpen(mLauncher) == null) {
mInScrollArea = true;
final int page = getNextPage() +
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 439e3145e..83391f3ec 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -192,8 +192,8 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
});
return true;
} else if (action == MOVE_TO_WORKSPACE) {
- Folder folder = mLauncher.getWorkspace().getOpenFolder();
- mLauncher.closeFolder(folder, true);
+ Folder folder = Folder.getOpen(mLauncher);
+ folder.close(true);
ShortcutInfo info = (ShortcutInfo) item;
folder.getInfo().remove(info, false);
@@ -373,12 +373,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY());
- Workspace workspace = mLauncher.getWorkspace();
-
- Folder folder = workspace.getOpenFolder();
+ Folder folder = Folder.getOpen(mLauncher);
if (folder != null) {
if (!folder.getItemsInReadingOrder().contains(item)) {
- mLauncher.closeFolder();
+ folder.close(true);
folder = null;
}
}
@@ -390,7 +388,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
if (folder != null) {
folder.startDrag(cellInfo.cell, options);
} else {
- workspace.startDrag(cellInfo, options);
+ mLauncher.getWorkspace().startDrag(cellInfo, options);
}
}
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index 5baa7b59e..f7ca7034d 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -19,6 +19,7 @@ package com.android.launcher3.accessibility;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherModel;
@@ -64,7 +65,7 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele
ArrayList<ItemInfo> itemList = new ArrayList<>();
itemList.add(info);
mLauncher.bindItems(itemList, 0, itemList.size(), true);
- mLauncher.closeShortcutsContainer();
+ AbstractFloatingView.closeAllOpenViews(mLauncher);
announceConfirmation(R.string.item_added_to_workspace);
}
};
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index cdd2f1d0c..689ee3816 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -52,6 +52,7 @@ import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.keyboard.FocusedItemDecorator;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ComponentKey;
@@ -265,7 +266,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
}
// IF a shortcuts container is open, container should not be pulled down.
- if (mLauncher.getOpenShortcutsContainer() != null) {
+ if (DeepShortcutsContainer.getOpen(mLauncher) != null) {
return false;
}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 1bbbd4615..019a1745c 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -49,9 +49,11 @@ import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
+import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetHostView;
@@ -66,7 +68,6 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
-import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
@@ -178,18 +179,13 @@ public class DragLayer extends InsettableFrameLayout {
}
public boolean isEventOverPageIndicator(MotionEvent ev) {
- getDescendantRectRelativeToSelf(mLauncher.getWorkspace().getPageIndicator(), mHitRect);
- return mHitRect.contains((int) ev.getX(), (int) ev.getY());
+ return isEventOverView(mLauncher.getWorkspace().getPageIndicator(), ev);
}
public boolean isEventOverHotseat(MotionEvent ev) {
return isEventOverView(mLauncher.getHotseat(), ev);
}
- private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
- return isEventOverView(folder.getEditTextRegion(), ev);
- }
-
private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
return isEventOverView(folder, ev);
}
@@ -204,45 +200,27 @@ public class DragLayer extends InsettableFrameLayout {
}
private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
- // Remove the shortcuts container when touching outside of it.
- DeepShortcutsContainer deepShortcutsContainer = mLauncher.getOpenShortcutsContainer();
- if (deepShortcutsContainer != null) {
- if (isEventOverView(deepShortcutsContainer, ev)) {
- // Let the container handle the event.
- return false;
- } else {
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
+ if (topView != null && intercept) {
+ ExtendedEditText textView = topView.getActiveTextView();
+ if (textView != null) {
+ if (!isEventOverView(textView, ev)) {
+ textView.dispatchBackKey();
+ return true;
+ }
+ } else if (!isEventOverView(topView, ev)) {
if (isInAccessibleDrag()) {
// Do not close the container if in drag and drop.
if (!isEventOverDropTargetBar(ev)) {
return true;
}
} else {
- mLauncher.closeShortcutsContainer();
+ topView.close(true);
+
// We let touches on the original icon go through so that users can launch
// the app with one tap if they don't find a shortcut they want.
- return !isEventOverView(deepShortcutsContainer.getOriginalIcon(), ev);
- }
- }
- }
-
- Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
- if (currentFolder != null && intercept) {
- if (currentFolder.isEditingName()) {
- if (!isEventOverFolderTextRegion(currentFolder, ev)) {
- currentFolder.dismissEditingName();
- return true;
- }
- }
-
- if (!isEventOverFolder(currentFolder, ev)) {
- if (isInAccessibleDrag()) {
- // Do not close the folder if in drag and drop.
- if (!isEventOverDropTargetBar(ev)) {
- return true;
- }
- } else {
- mLauncher.closeFolder();
- return true;
+ View extendedTouch = topView.getExtendedTouchView();
+ return extendedTouch == null || !isEventOverView(extendedTouch, ev);
}
}
}
@@ -300,7 +278,7 @@ public class DragLayer extends InsettableFrameLayout {
if (mLauncher == null || mLauncher.getWorkspace() == null) {
return false;
}
- Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
+ Folder currentFolder = Folder.getOpen(mLauncher);
if (currentFolder == null) {
return false;
} else {
@@ -350,7 +328,7 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
// Shortcuts can appear above folder
- View topView = mLauncher.getTopFloatingView();
+ View topView = AbstractFloatingView.getTopOpenView(mLauncher);
if (topView != null) {
if (child == topView) {
return super.onRequestSendAccessibilityEvent(child, event);
@@ -367,7 +345,7 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
- View topView = mLauncher.getTopFloatingView();
+ View topView = AbstractFloatingView.getTopOpenView(mLauncher);
if (topView != null) {
// Only add the top view as a child for accessibility when it is open
childrenForAccessibility.add(topView);
@@ -522,7 +500,7 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
// Consume the unhandled move if a container is open, to avoid switching pages underneath.
- boolean isContainerOpen = mLauncher.getTopFloatingView() != null;
+ boolean isContainerOpen = AbstractFloatingView.getTopOpenView(mLauncher) != null;
return isContainerOpen || mDragController.dispatchUnhandledMove(focused, direction);
}
@@ -1031,7 +1009,7 @@ public class DragLayer extends InsettableFrameLayout {
@Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
- View topView = mLauncher.getTopFloatingView();
+ View topView = AbstractFloatingView.getTopOpenView(mLauncher);
if (topView != null) {
return topView.requestFocus(direction, previouslyFocusedRect);
} else {
@@ -1041,7 +1019,7 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
- View topView = mLauncher.getTopFloatingView();
+ View topView = AbstractFloatingView.getTopOpenView(mLauncher);
if (topView != null) {
topView.addFocusables(views, direction);
} else {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 7030b660f..0685ddbe1 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -46,9 +46,9 @@ import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
-import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Alarm;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
@@ -73,6 +73,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragLayer;
@@ -91,9 +92,10 @@ import java.util.Comparator;
/**
* Represents a set of icons chosen by the user or generated by the system.
*/
-public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
+public class Folder extends AbstractFloatingView implements DragSource, View.OnClickListener,
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
- View.OnFocusChangeListener, DragListener, DropTargetSource {
+ View.OnFocusChangeListener, DragListener, DropTargetSource,
+ ExtendedEditText.OnBackKeyListener {
private static final String TAG = "Launcher.Folder";
/**
@@ -227,14 +229,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mPageIndicator = (PageIndicatorDots) findViewById(R.id.folder_page_indicator);
mFolderName = (ExtendedEditText) findViewById(R.id.folder_name);
- mFolderName.setOnBackKeyListener(new ExtendedEditText.OnBackKeyListener() {
- @Override
- public boolean onBackKey() {
- // Close the activity on back key press
- doneEditingFolderName(true);
- return false;
- }
- });
+ mFolderName.setOnBackKeyListener(this);
mFolderName.setOnFocusChangeListener(this);
if (!Utilities.ATLEAST_MARSHMALLOW) {
@@ -357,12 +352,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
});
}
- public void dismissEditingName() {
- mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
- doneEditingFolderName(true);
- }
- public void doneEditingFolderName(boolean commit) {
+ @Override
+ public boolean onBackKey() {
mFolderName.setHint(sHintText);
// Convert to a string here to ensure that no other state associated with the text field
// gets saved.
@@ -370,30 +362,30 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mInfo.setTitle(newTitle);
LauncherModel.updateItemInDatabase(mLauncher, mInfo);
- if (commit) {
- Utilities.sendCustomAccessibilityEvent(
- this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
- getContext().getString(R.string.folder_renamed, newTitle));
- }
+ Utilities.sendCustomAccessibilityEvent(
+ this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ getContext().getString(R.string.folder_renamed, newTitle));
// This ensures that focus is gained every time the field is clicked, which selects all
// the text and brings up the soft keyboard if necessary.
mFolderName.clearFocus();
- Selection.setSelection((Spannable) mFolderName.getText(), 0, 0);
+ Selection.setSelection(mFolderName.getText(), 0, 0);
mIsEditingName = false;
+ return true;
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
- dismissEditingName();
+ mFolderName.dispatchBackKey();
return true;
}
return false;
}
- public View getEditTextRegion() {
- return mFolderName;
+ @Override
+ public ExtendedEditText getActiveTextView() {
+ return isEditingName() ? mFolderName : null;
}
/**
@@ -518,8 +510,33 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mState = STATE_SMALL;
}
+ /**
+ * Opens the user folder described by the specified tag. The opening of the folder
+ * is animated relative to the specified View. If the View is null, no animation
+ * is played.
+ */
public void animateOpen() {
- if (!(getParent() instanceof DragLayer)) return;
+ Folder openFolder = getOpen(mLauncher);
+ if (openFolder != null && openFolder != this) {
+ // Close any open folder before opening a folder.
+ openFolder.close(true);
+ }
+
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ // Just verify that the folder hasn't already been added to the DragLayer.
+ // There was a one-off crash where the folder had a parent already.
+ if (getParent() == null) {
+ dragLayer.addView(this);
+ mDragController.addDropTarget(this);
+ } else {
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
+ + getParent());
+ }
+ }
+
+ mIsOpen = true;
+ mFolderIcon.growAndFadeOut();
mContent.completePendingPageChanges();
if (!mDragInProgress) {
@@ -532,83 +549,63 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
mDeleteFolderOnDropCompleted = false;
- Animator openFolderAnim = null;
final Runnable onCompleteRunnable;
- if (!Utilities.ATLEAST_LOLLIPOP) {
- positionAndSizeAsIcon();
- centerAboutIcon();
+ prepareReveal();
+ centerAboutIcon();
- final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 1, 1, 1);
- oa.setDuration(mExpandDuration);
- openFolderAnim = oa;
+ AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
+ int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
+ int height = getFolderHeight();
- setLayerType(LAYER_TYPE_HARDWARE, null);
- onCompleteRunnable = new Runnable() {
- @Override
- public void run() {
- setLayerType(LAYER_TYPE_NONE, null);
- }
- };
- } else {
- prepareReveal();
- centerAboutIcon();
-
- AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
- int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
- int height = getFolderHeight();
-
- float transX = - 0.075f * (width / 2 - getPivotX());
- float transY = - 0.075f * (height / 2 - getPivotY());
- setTranslationX(transX);
- setTranslationY(transY);
- PropertyValuesHolder tx = PropertyValuesHolder.ofFloat(TRANSLATION_X, transX, 0);
- PropertyValuesHolder ty = PropertyValuesHolder.ofFloat(TRANSLATION_Y, transY, 0);
-
- Animator drift = ObjectAnimator.ofPropertyValuesHolder(this, tx, ty);
- drift.setDuration(mMaterialExpandDuration);
- drift.setStartDelay(mMaterialExpandStagger);
- drift.setInterpolator(new LogDecelerateInterpolator(100, 0));
-
- int rx = (int) Math.max(Math.max(width - getPivotX(), 0), getPivotX());
- int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY());
- float radius = (float) Math.hypot(rx, ry);
-
- Animator reveal = new CircleRevealOutlineProvider((int) getPivotX(),
- (int) getPivotY(), 0, radius).createRevealAnimator(this);
- reveal.setDuration(mMaterialExpandDuration);
- reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
-
- mContent.setAlpha(0f);
- Animator iconsAlpha = ObjectAnimator.ofFloat(mContent, "alpha", 0f, 1f);
- iconsAlpha.setDuration(mMaterialExpandDuration);
- iconsAlpha.setStartDelay(mMaterialExpandStagger);
- iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
-
- mFooter.setAlpha(0f);
- Animator textAlpha = ObjectAnimator.ofFloat(mFooter, "alpha", 0f, 1f);
- textAlpha.setDuration(mMaterialExpandDuration);
- textAlpha.setStartDelay(mMaterialExpandStagger);
- textAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
-
- anim.play(drift);
- anim.play(iconsAlpha);
- anim.play(textAlpha);
- anim.play(reveal);
-
- openFolderAnim = anim;
-
- mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
- mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
- onCompleteRunnable = new Runnable() {
- @Override
- public void run() {
- mContent.setLayerType(LAYER_TYPE_NONE, null);
- mFooter.setLayerType(LAYER_TYPE_NONE, null);
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
- }
- };
- }
- openFolderAnim.addListener(new AnimatorListenerAdapter() {
+ float transX = - 0.075f * (width / 2 - getPivotX());
+ float transY = - 0.075f * (height / 2 - getPivotY());
+ setTranslationX(transX);
+ setTranslationY(transY);
+ PropertyValuesHolder tx = PropertyValuesHolder.ofFloat(TRANSLATION_X, transX, 0);
+ PropertyValuesHolder ty = PropertyValuesHolder.ofFloat(TRANSLATION_Y, transY, 0);
+
+ Animator drift = ObjectAnimator.ofPropertyValuesHolder(this, tx, ty);
+ drift.setDuration(mMaterialExpandDuration);
+ drift.setStartDelay(mMaterialExpandStagger);
+ drift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+ int rx = (int) Math.max(Math.max(width - getPivotX(), 0), getPivotX());
+ int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY());
+ float radius = (float) Math.hypot(rx, ry);
+
+ Animator reveal = new CircleRevealOutlineProvider((int) getPivotX(),
+ (int) getPivotY(), 0, radius).createRevealAnimator(this);
+ reveal.setDuration(mMaterialExpandDuration);
+ reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+ mContent.setAlpha(0f);
+ Animator iconsAlpha = ObjectAnimator.ofFloat(mContent, "alpha", 0f, 1f);
+ iconsAlpha.setDuration(mMaterialExpandDuration);
+ iconsAlpha.setStartDelay(mMaterialExpandStagger);
+ iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
+
+ mFooter.setAlpha(0f);
+ Animator textAlpha = ObjectAnimator.ofFloat(mFooter, "alpha", 0f, 1f);
+ textAlpha.setDuration(mMaterialExpandDuration);
+ textAlpha.setStartDelay(mMaterialExpandStagger);
+ textAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
+
+ anim.play(drift);
+ anim.play(iconsAlpha);
+ anim.play(textAlpha);
+ anim.play(reveal);
+
+ mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
+ mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
+ onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mContent.setLayerType(LAYER_TYPE_NONE, null);
+ mFooter.setLayerType(LAYER_TYPE_NONE, null);
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ }
+ };
+ anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
Utilities.sendCustomAccessibilityEvent(
@@ -639,7 +636,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// Do not update the flag if we are in drag mode. The flag will be updated, when we
// actually drop the icon.
final boolean updateAnimationFlag = !mDragInProgress;
- openFolderAnim.addListener(new AnimatorListenerAdapter() {
+ anim.addListener(new AnimatorListenerAdapter() {
@SuppressLint("InlinedApi")
@Override
@@ -662,7 +659,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
mPageIndicator.stopAllAnimations();
- openFolderAnim.start();
+ anim.start();
// Make sure the folder picks up the last drag move even if the finger doesn't move.
if (mDragController.isDragging()) {
@@ -670,6 +667,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
mContent.verifyVisibleHighResIcons(mContent.getNextPage());
+
+ // Notify the accessibility manager that this folder "window" has appeared and occluded
+ // the workspace items
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ dragLayer.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
public void beginExternalDrag() {
@@ -682,14 +684,44 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mDragController.addDragListener(this);
}
- public void animateClosed() {
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_FOLDER) != 0;
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ mIsOpen = false;
+
+ if (isEditingName()) {
+ mFolderName.dispatchBackKey();
+ }
+
+ if (mFolderIcon != null) {
+ mFolderIcon.shrinkAndFadeIn(animate);
+ }
+
if (!(getParent() instanceof DragLayer)) return;
+
+ if (animate) {
+ animateClosed();
+ } else {
+ closeComplete(false);
+ }
+
+ // Notify the accessibility manager that this folder "window" has disappeared and no
+ // longer occludes the workspace items
+ ((DragLayer) getParent())
+ .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+
+ private void animateClosed() {
final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f);
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
setLayerType(LAYER_TYPE_NONE, null);
- close(true);
+ closeComplete(true);
}
@Override
public void onAnimationStart(Animator animation) {
@@ -705,7 +737,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
oa.start();
}
- public void close(boolean wasAnimated) {
+ private void closeComplete(boolean wasAnimated) {
// TODO: Clear all active animations.
DragLayer parent = (DragLayer) getParent();
if (parent != null) {
@@ -840,8 +872,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
};
public void completeDragExit() {
- if (mInfo.opened) {
- mLauncher.closeFolder();
+ if (mIsOpen) {
+ close(true);
mRearrangeOnClose = true;
} else if (mState == STATE_ANIMATING) {
mRearrangeOnClose = true;
@@ -1370,8 +1402,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
rearrangeChildren();
}
if (getItemCount() <= 1) {
- if (mInfo.opened) {
- mLauncher.closeFolder(this, true);
+ if (mIsOpen) {
+ close(true);
} else {
replaceFolderWithFinalItem();
}
@@ -1417,7 +1449,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
if (hasFocus) {
startEditingFolderName();
} else {
- dismissEditingName();
+ mFolderName.dispatchBackKey();
}
}
}
@@ -1516,4 +1548,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
updateTextViewFocus();
}
}
+
+ /**
+ * Returns a folder which is already open or null
+ */
+ public static Folder getOpen(Launcher launcher) {
+ return getOpenView(launcher, TYPE_FOLDER);
+ }
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 69c2b0fa3..a29a94659 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -18,6 +18,7 @@ package com.android.launcher3.folder;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
@@ -56,7 +57,6 @@ import com.android.launcher3.IconCache;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.PreloadIconDrawable;
@@ -142,6 +142,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
mPreviewLayoutRule = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
new StackFolderIconLayoutRule() :
new ClippedFolderIconLayoutRule();
+ mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
@@ -202,16 +203,12 @@ public class FolderIcon extends FrameLayout implements FolderListener {
updateItemDrawingParams(false);
}
- public FolderInfo getFolderInfo() {
- return mInfo;
- }
-
private boolean willAcceptItem(ItemInfo item) {
final int itemType = item.itemType;
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
- !mFolder.isFull() && item != mInfo && !mInfo.opened);
+ !mFolder.isFull() && item != mInfo && !mFolder.isOpen());
}
public boolean acceptDrop(ItemInfo dragInfo) {
@@ -243,7 +240,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
OnAlarmListener mOnOpenListener = new OnAlarmListener() {
public void onAlarm(Alarm alarm) {
mFolder.beginExternalDrag();
- mLauncher.openFolder(FolderIcon.this);
+ mFolder.animateOpen();
}
};
@@ -974,12 +971,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- }
-
- @Override
public void cancelLongPress() {
super.cancelLongPress();
mLongPressHelper.cancelLongPress();
@@ -990,13 +981,76 @@ public class FolderIcon extends FrameLayout implements FolderListener {
mInfo.removeListener(mFolder);
}
+ public void shrinkAndFadeIn(boolean animate) {
+ final CellLayout cl = (CellLayout) getParent().getParent();
+ ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
+
+ // We remove and re-draw the FolderIcon in-case it has changed
+ final PreviewImageView previewImage = PreviewImageView.get(getContext());
+ previewImage.removeFromParent();
+ copyToPreview(previewImage);
+
+ if (cl != null) {
+ cl.clearFolderLeaveBehind();
+ }
+
+ ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 1, 1, 1);
+ oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
+ oa.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (cl != null) {
+ // Remove the ImageView copy of the FolderIcon and make the original visible.
+ previewImage.removeFromParent();
+ setVisibility(View.VISIBLE);
+ }
+ }
+ });
+ oa.start();
+ if (!animate) {
+ oa.end();
+ }
+ }
+
+ public void growAndFadeOut() {
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+ // While the folder is open, the position of the icon cannot change.
+ lp.canReorder = false;
+ if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ CellLayout cl = (CellLayout) getParent().getParent();
+ cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
+ }
+
+ // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
+ PreviewImageView previewImage = PreviewImageView.get(getContext());
+ copyToPreview(previewImage);
+ setVisibility(View.INVISIBLE);
+
+ ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 0, 1.5f, 1.5f);
+ oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
+ oa.start();
+ }
+
+ /**
+ * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
+ * in the DragLayer in the exact absolute location of the original FolderIcon.
+ */
+ private void copyToPreview(PreviewImageView previewImageView) {
+ previewImageView.copy(this);
+ if (mFolder != null) {
+ previewImageView.setPivotX(mFolder.getPivotXForIconAnimation());
+ previewImageView.setPivotY(mFolder.getPivotYForIconAnimation());
+ mFolder.bringToFront();
+ }
+ }
+
public interface PreviewLayoutRule {
- public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+ PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
PreviewItemDrawingParams params);
- public void init(int availableSpace, int intrinsicIconSize, boolean rtl);
+ void init(int availableSpace, int intrinsicIconSize, boolean rtl);
- public int numItems();
- public boolean clipToBackground();
+ int numItems();
+ boolean clipToBackground();
}
}
diff --git a/src/com/android/launcher3/folder/PreviewImageView.java b/src/com/android/launcher3/folder/PreviewImageView.java
new file mode 100644
index 000000000..c4f3ee15c
--- /dev/null
+++ b/src/com/android/launcher3/folder/PreviewImageView.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.folder;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.DragLayer;
+
+/**
+ * A temporary view which displays the a bitmap (used for folder icon animation)
+ */
+public class PreviewImageView extends ImageView {
+
+ private final Rect mTempRect = new Rect();
+ private final DragLayer mParent;
+
+ private Bitmap mBitmap;
+ private Canvas mCanvas;
+
+ public PreviewImageView(DragLayer parent) {
+ super(parent.getContext());
+ mParent = parent;
+ }
+
+ public void copy(View view) {
+ final int width = view.getMeasuredWidth();
+ final int height = view.getMeasuredHeight();
+
+ if (mBitmap == null || mBitmap.getWidth() != width || mBitmap.getHeight() != height) {
+ mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ mCanvas = new Canvas(mBitmap);
+ }
+
+ DragLayer.LayoutParams lp;
+ if (getLayoutParams() instanceof DragLayer.LayoutParams) {
+ lp = (DragLayer.LayoutParams) getLayoutParams();
+ } else {
+ lp = new DragLayer.LayoutParams(width, height);
+ }
+
+ // The layout from which the folder is being opened may be scaled, adjust the starting
+ // view size by this scale factor.
+ float scale = mParent.getDescendantRectRelativeToSelf(view, mTempRect);
+ lp.customPosition = true;
+ lp.x = mTempRect.left;
+ lp.y = mTempRect.top;
+ lp.width = (int) (scale * width);
+ lp.height = (int) (scale * height);
+
+ mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ view.draw(mCanvas);
+ setImageBitmap(mBitmap);
+
+ // Just in case this image view is still in the drag layer from a previous animation,
+ // we remove it and re-add it.
+ removeFromParent();
+ mParent.addView(this, lp);
+ }
+
+ public void removeFromParent() {
+ if (mParent.indexOfChild(this) != -1) {
+ mParent.removeView(this);
+ }
+ }
+
+ public static PreviewImageView get(Context context) {
+ DragLayer dragLayer = Launcher.getLauncher(context).getDragLayer();
+ PreviewImageView view = (PreviewImageView) dragLayer.getTag(R.id.preview_image_id);
+ if (view == null) {
+ view = new PreviewImageView(dragLayer);
+ dragLayer.setTag(R.id.preview_image_id, view);
+ }
+ return view;
+ }
+}
diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
index 6056f4c10..6603e93b0 100644
--- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java
+++ b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
@@ -46,7 +46,7 @@ public class CustomActionsPopup implements OnMenuItemClickListener {
public CustomActionsPopup(Launcher launcher, View icon) {
mLauncher = launcher;
mIcon = icon;
- DeepShortcutsContainer container = launcher.getOpenShortcutsContainer();
+ DeepShortcutsContainer container = DeepShortcutsContainer.getOpen(launcher);
if (container != null) {
mDelegate = container.getAccessibilityDelegate();
} else {
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 9f9a99eb1..b5126e95c 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -41,6 +41,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
@@ -73,9 +74,9 @@ import java.util.List;
* A container for shortcuts to deep links within apps.
*/
@TargetApi(Build.VERSION_CODES.N)
-public class DeepShortcutsContainer extends LinearLayout implements View.OnLongClickListener,
+public class DeepShortcutsContainer extends AbstractFloatingView
+ implements View.OnLongClickListener,
View.OnTouchListener, DragSource, DragController.DragListener {
- private static final String TAG = "ShortcutsContainer";
private final Point mIconShift = new Point();
@@ -94,7 +95,6 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
private Animator mOpenCloseAnimator;
private boolean mDeferContainerRemoval;
- private boolean mIsOpen;
public DeepShortcutsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -376,7 +376,8 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
return arrowView;
}
- public BubbleTextView getOriginalIcon() {
+ @Override
+ public View getExtendedTouchView() {
return mOriginalIcon;
}
@@ -444,7 +445,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
dv.animateShift(-mIconShift.x, -mIconShift.y);
// TODO: support dragging from within folder without having to close it
- mLauncher.closeFolder();
+ AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_FOLDER);
return false;
}
@@ -499,7 +500,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
} else {
// Close animation is not running.
if (mDeferContainerRemoval) {
- close();
+ closeComplete();
}
}
}
@@ -512,7 +513,16 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
targetParent.containerType = LauncherLogProto.DEEPSHORTCUTS;
}
- public void animateClose() {
+ @Override
+ protected void handleClose(boolean animate) {
+ if (animate) {
+ animateClose();
+ } else {
+ closeComplete();
+ }
+ }
+
+ private void animateClose() {
if (!mIsOpen) {
return;
}
@@ -592,7 +602,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
if (mDeferContainerRemoval) {
setVisibility(INVISIBLE);
} else {
- close();
+ closeComplete();
}
}
});
@@ -607,7 +617,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
/**
* Closes the folder without animation.
*/
- public void close() {
+ private void closeComplete() {
if (mOpenCloseAnimator != null) {
mOpenCloseAnimator.cancel();
mOpenCloseAnimator = null;
@@ -621,8 +631,9 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
mLauncher.getDragLayer().removeView(this);
}
- public boolean isOpen() {
- return mIsOpen;
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_DEEPSHORTCUT_CONTAINER) != 0;
}
/**
@@ -631,7 +642,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
*/
public static DeepShortcutsContainer showForIcon(BubbleTextView icon) {
Launcher launcher = Launcher.getLauncher(icon.getContext());
- if (launcher.getOpenShortcutsContainer() != null) {
+ if (getOpen(launcher) != null) {
// There is already a shortcuts container open, so don't open this one.
icon.clearFocus();
return null;
@@ -666,4 +677,11 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC
return unbadgedBitmap;
}
}
+
+ /**
+ * Returns a DeepShortcutsContainer which is already open or null
+ */
+ public static DeepShortcutsContainer getOpen(Launcher launcher) {
+ return getOpenView(launcher, TYPE_DEEPSHORTCUT_CONTAINER);
+ }
}