Merge "Prevent opening shortcuts container if one is already open." into ub-launcher3-calgary
diff --git a/proguard.flags b/proguard.flags
index 19d2f0c..c5e9db1 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -66,6 +66,11 @@
public void setAnimationProgress(float);
}
+-keep class com.android.launcher3.pageindicators.CaretDrawable {
+ public float getCaretProgress();
+ public void setCaretProgress(float);
+}
+
-keep class com.android.launcher3.Workspace {
public float getBackgroundAlpha();
public void setBackgroundAlpha(float);
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 4fcbbd2..2d66d72 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -21,8 +21,18 @@
android:elevation="@dimen/deep_shortcuts_elevation"
android:background="@drawable/bg_white_pill">
- <com.android.launcher3.shortcuts.DeepShortcutTextView
+<com.android.launcher3.shortcuts.DeepShortcutTextView
android:id="@+id/deep_shortcut"
style="@style/Icon.DeepShortcut"
- android:focusable="true"/>
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true" />
+
+ <View
+ android:id="@+id/deep_shortcut_icon"
+ android:layout_width="@dimen/deep_shortcut_icon_size"
+ android:layout_height="@dimen/deep_shortcut_icon_size"
+ android:layout_margin="@dimen/deep_shortcut_padding_start"
+ android:layout_gravity="start" />
+
</com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/deep_shortcuts_container.xml b/res/layout/deep_shortcuts_container.xml
index 0441995..92e7a83 100644
--- a/res/layout/deep_shortcuts_container.xml
+++ b/res/layout/deep_shortcuts_container.xml
@@ -19,6 +19,7 @@
android:id="@+id/deep_shortcuts_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:clipChildren="false"
android:elevation="@dimen/deep_shortcuts_elevation"
android:orientation="vertical">
diff --git a/res/values/config.xml b/res/values/config.xml
index 9438042..94f02f9 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -88,9 +88,10 @@
<item type="id" name="cell_layout_jail_id" />
<!-- Deep shortcuts -->
- <integer name="config_deepShortcutOpenDuration">300</integer>
- <integer name="config_deepShortcutOpenStagger">20</integer>
- <integer name="config_deepShortcutHoverDuration">120</integer>
+ <integer name="config_deepShortcutOpenDuration">220</integer>
+ <integer name="config_deepShortcutOpenStagger">40</integer>
+ <integer name="config_deepShortcutCloseDuration">150</integer>
+ <integer name="config_deepShortcutCloseStagger">20</integer>
<!-- Accessibility actions -->
<item type="id" name="action_remove" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9f30752..2bc0cae 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -35,6 +35,8 @@
<string name="safemode_shortcut_error">Downloaded app disabled in Safe mode</string>
<!-- SafeMode widget error string -->
<string name="safemode_widget_error">Widgets disabled in Safe mode</string>
+ <!-- Message shown when a shortcut is not available. It could have been temporarily disabled and may start working again after some time. -->
+ <string name="shortcut_not_available">Shortcut isn\'t available</string>
<!-- Widgets -->
<!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a647cf2..4c0230a 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -80,8 +80,9 @@
<style name="Icon.DeepShortcut">
<item name="android:gravity">start|center_vertical</item>
<item name="android:elevation">@dimen/deep_shortcuts_elevation</item>
- <item name="android:paddingStart">@dimen/deep_shortcut_padding_start</item>
+ <item name="android:paddingStart">@dimen/bg_pill_height</item>
<item name="android:paddingEnd">@dimen/deep_shortcut_padding_end</item>
+ <item name="android:drawableEnd">@drawable/deep_shortcuts_drag_handle</item>
<item name="android:drawablePadding">@dimen/deep_shortcut_drawable_padding</item>
<item name="android:textColor">@color/quantum_panel_text_color</item>
<item name="android:shadowRadius">0</item>
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
index 37584fe..b4567c5 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
@@ -32,9 +32,9 @@
private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f;
- private static final int SHADOW_INSET = 5;
- private static final int SHADOW_SHIFT_Y = 4;
- private static final float SHADOW_ALPHA_MULTIPLIER = 0.5f;
+ private static final int SHADOW_INSET = 3;
+ private static final int SHADOW_SHIFT_Y = 2;
+ private static final float SHADOW_ALPHA_MULTIPLIER = 0.67f;
private Resources mRes;
private BaseRecyclerView mRv;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index deac73b..78e0aa0 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -465,7 +465,11 @@
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
- mModel.startLoader(mWorkspace.getRestorePage());
+ if (!mModel.startLoader(mWorkspace.getRestorePage())) {
+ // If we are not binding synchronously, show a fade in animation when
+ // the first page bind completes.
+ mDragLayer.setAlpha(0);
+ }
}
}
@@ -997,8 +1001,8 @@
// Don't update the predicted apps if the user is returning to launcher in the apps
// view after launching an app, as they may be depending on the UI to be static to
// switch to another app, otherwise, if it was
- showAppsView(false /* animated */, false /* resetListToTop */,
- !launchedFromApp /* updatePredictedApps */, false /* focusSearchBar */);
+ showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */,
+ false /* focusSearchBar */);
} else if (mOnResumeState == State.WIDGETS) {
showWidgetsView(false, false);
}
@@ -1864,7 +1868,7 @@
mWorkspace.exitWidgetResizeMode();
closeFolder(alreadyOnHome);
- closeShortcutsContainer();
+ closeShortcutsContainer(alreadyOnHome);
exitSpringLoadedDragMode();
// If we are already on home, then just animate back to the workspace,
@@ -1951,8 +1955,7 @@
// this state is reflected.
// TODO: Move folderInfo.isOpened out of the model and make it a UI state.
closeFolder(false);
-
- closeShortcutsContainer();
+ closeShortcutsContainer(false);
if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
mWaitingForResult) {
@@ -2601,8 +2604,8 @@
if (!isAppsViewVisible()) {
getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.TAP,
LauncherLogProto.ALL_APPS_BUTTON);
- showAppsView(true /* animated */, false /* resetListToTop */,
- true /* updatePredictedApps */, false /* focusSearchBar */);
+ showAppsView(true /* animated */, true /* updatePredictedApps */,
+ false /* focusSearchBar */);
}
}
@@ -2611,7 +2614,7 @@
if (!isAppsViewVisible()) {
getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.LONGPRESS,
LauncherLogProto.ALL_APPS_BUTTON);
- showAppsView(true /* animated */, false /* resetListToTop */,
+ showAppsView(true /* animated */,
true /* updatePredictedApps */, true /* focusSearchBar */);
}
}
@@ -2656,6 +2659,8 @@
int error = R.string.activity_not_available;
if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) {
error = R.string.safemode_shortcut_error;
+ } else if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_BY_PUBLISHER) != 0) {
+ error = R.string.shortcut_not_available;
}
Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
return;
@@ -3130,10 +3135,17 @@
}
public void closeShortcutsContainer() {
+ closeShortcutsContainer(true);
+ }
+
+ public void closeShortcutsContainer(boolean animate) {
DeepShortcutsContainer deepShortcutsContainer = getOpenShortcutsContainer();
if (deepShortcutsContainer != null) {
- mDragController.removeDragListener(deepShortcutsContainer);
- mDragLayer.removeView(deepShortcutsContainer);
+ if (animate) {
+ deepShortcutsContainer.animateClose();
+ } else {
+ deepShortcutsContainer.close();
+ }
}
}
@@ -3145,7 +3157,8 @@
// 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) {
+ if (child instanceof DeepShortcutsContainer
+ && ((DeepShortcutsContainer) child).isOpen()) {
return (DeepShortcutsContainer) child;
}
}
@@ -3355,12 +3368,9 @@
/**
* Shows the apps view.
*/
- public void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps,
+ public void showAppsView(boolean animated, boolean updatePredictedApps,
boolean focusSearchBar) {
markAppsViewShown();
- if (resetListToTop) {
- mAppsView.scrollToTop();
- }
if (updatePredictedApps) {
tryAndUpdatePredictedApps();
}
@@ -3487,7 +3497,7 @@
void exitSpringLoadedDragMode() {
if (mState == State.APPS_SPRING_LOADED) {
- showAppsView(true /* animated */, false /* resetListToTop */,
+ showAppsView(true /* animated */,
false /* updatePredictedApps */, false /* focusSearchBar */);
} else if (mState == State.WIDGETS_SPRING_LOADED) {
showWidgetsView(true, false);
@@ -3999,6 +4009,32 @@
}
}
+ @Override
+ public void finishFirstPageBind(final ViewOnDrawExecutor executor) {
+ Runnable r = new Runnable() {
+ public void run() {
+ finishFirstPageBind(executor);
+ }
+ };
+ if (waitUntilResume(r)) {
+ return;
+ }
+
+ Runnable onComplete = new Runnable() {
+ @Override
+ public void run() {
+ if (executor != null) {
+ executor.onLoadAnimationCompleted();
+ }
+ }
+ };
+ if (mDragLayer.getAlpha() < 1) {
+ mDragLayer.animate().alpha(1).withEndAction(onComplete).start();
+ } else {
+ onComplete.run();
+ }
+ }
+
/**
* Callback saying that there aren't any more items to bind.
*
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 4561111..b465b3a 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -195,6 +195,7 @@
public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
boolean forceAnimateIcons);
public void bindScreens(ArrayList<Long> orderedScreenIds);
+ public void finishFirstPageBind(ViewOnDrawExecutor executor);
public void finishBindingItems();
public void bindAppWidget(LauncherAppWidgetInfo info);
public void bindAllApplications(ArrayList<AppInfo> apps);
@@ -1282,7 +1283,11 @@
return (mCallbacks != null && mCallbacks.get() == callbacks);
}
- public void startLoader(int synchronousBindPage) {
+ /**
+ * Starts the loader. Tries to bind {@params synchronousBindPage} synchronously if possible.
+ * @return true if the page could be bound synchronously.
+ */
+ public boolean startLoader(int synchronousBindPage) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
InstallShortcutReceiver.enableInstallQueue();
synchronized (mLock) {
@@ -1302,12 +1307,14 @@
if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE && mAllAppsLoaded
&& mWorkspaceLoaded && mDeepShortcutsLoaded && !mIsLoaderTaskRunning) {
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
+ return true;
} else {
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
}
}
}
+ return false;
}
public void stopLoader() {
@@ -2508,17 +2515,19 @@
orderedScreenIds.addAll(sBgWorkspaceScreens);
}
- final boolean isLoadingSynchronously =
- synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
- int currScreen = isLoadingSynchronously ? synchronizeBindPage :
- oldCallbacks.getCurrentWorkspaceScreen();
- if (currScreen >= orderedScreenIds.size()) {
- // There may be no workspace screens (just hotseat items and an empty page).
- currScreen = PagedView.INVALID_RESTORE_PAGE;
+ final int currentScreen;
+ {
+ int currScreen = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE
+ ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen();
+ if (currScreen >= orderedScreenIds.size()) {
+ // There may be no workspace screens (just hotseat items and an empty page).
+ currScreen = PagedView.INVALID_RESTORE_PAGE;
+ }
+ currentScreen = currScreen;
}
- final int currentScreen = currScreen;
- final long currentScreenId = currentScreen < 0
- ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
+ final boolean validFirstPage = currentScreen >= 0;
+ final long currentScreenId =
+ validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;
// Separate the items that are on the current screen, and all the other remaining items
ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
@@ -2551,12 +2560,24 @@
// Load items on the current page.
bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, mainExecutor);
- // In case of isLoadingSynchronously, only bind the first screen, and defer binding the
- // remaining screens after first onDraw is called. This ensures that the first screen
- // is immediately visible (eg. during rotation)
- // In case of !isLoadingSynchronously, bind all pages one after other.
- final Executor deferredExecutor = isLoadingSynchronously ?
- new ViewOnDrawExecutor(mHandler) : mainExecutor;
+ // In case of validFirstPage, only bind the first screen, and defer binding the
+ // remaining screens after first onDraw (and an optional the fade animation whichever
+ // happens later).
+ // This ensures that the first screen is immediately visible (eg. during rotation)
+ // In case of !validFirstPage, bind all pages one after other.
+ final Executor deferredExecutor =
+ validFirstPage ? new ViewOnDrawExecutor(mHandler) : mainExecutor;
+
+ mainExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+ if (callbacks != null) {
+ callbacks.finishFirstPageBind(
+ validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null);
+ }
+ }
+ });
bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, deferredExecutor);
@@ -2590,7 +2611,7 @@
};
deferredExecutor.execute(r);
- if (isLoadingSynchronously) {
+ if (validFirstPage) {
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
@@ -2821,9 +2842,13 @@
// Now add the new shortcuts to the map.
for (ShortcutInfoCompat shortcut : shortcuts) {
- ComponentKey targetComponent
- = new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
- mBgDeepShortcutMap.addToList(targetComponent, shortcut.getId());
+ boolean shouldShowInContainer = shortcut.isEnabled()
+ && (shortcut.isDeclaredInManifest() || shortcut.isDynamic());
+ if (shouldShowInContainer) {
+ ComponentKey targetComponent
+ = new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
+ mBgDeepShortcutMap.addToList(targetComponent, shortcut.getId());
+ }
}
}
@@ -3332,9 +3357,11 @@
for (ItemInfo itemInfo : sBgItemsIdMap) {
if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
ShortcutInfo si = (ShortcutInfo) itemInfo;
- String shortcutId = si.getDeepShortcutId();
- if (idsToShortcuts.containsKey(shortcutId)) {
- idsToWorkspaceShortcutInfos.addToList(shortcutId, si);
+ if (si.getIntent().getPackage().equals(mPackageName) && si.user.equals(mUser)) {
+ String shortcutId = si.getDeepShortcutId();
+ if (idsToShortcuts.containsKey(shortcutId)) {
+ idsToWorkspaceShortcutInfos.addToList(shortcutId, si);
+ }
}
}
}
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index b0358e8..b6e2e4c 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -705,7 +705,7 @@
if (!animated || !initialized) {
if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
- mAllAppsController.finishPullDown(false);
+ mAllAppsController.finishPullDown();
}
fromView.setVisibility(View.GONE);
dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 0cc5a1b..00ac9bd 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -28,7 +28,6 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.folder.FolderIcon;
@@ -101,22 +100,28 @@
/**
* Indicates that the icon is disabled due to safe mode restrictions.
*/
- public static final int FLAG_DISABLED_SAFEMODE = 1;
+ public static final int FLAG_DISABLED_SAFEMODE = 1 << 0;
/**
* Indicates that the icon is disabled as the app is not available.
*/
- public static final int FLAG_DISABLED_NOT_AVAILABLE = 2;
+ public static final int FLAG_DISABLED_NOT_AVAILABLE = 1 << 1;
/**
* Indicates that the icon is disabled as the app is suspended
*/
- public static final int FLAG_DISABLED_SUSPENDED = 4;
+ public static final int FLAG_DISABLED_SUSPENDED = 1 << 2;
/**
* Indicates that the icon is disabled as the user is in quiet mode.
*/
- public static final int FLAG_DISABLED_QUIET_USER = 8;
+ public static final int FLAG_DISABLED_QUIET_USER = 1 << 3;
+
+
+ /**
+ * Indicates that the icon is disabled as the publisher has disabled the actual shortcut.
+ */
+ public static final int FLAG_DISABLED_BY_PUBLISHER = 1 << 4;
/**
* Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when
@@ -294,6 +299,11 @@
}
contentDescription = UserManagerCompat.getInstance(context)
.getBadgedLabelForUser(label, user);
+ if (shortcutInfo.isEnabled()) {
+ isDisabled &= ~FLAG_DISABLED_BY_PUBLISHER;
+ } else {
+ isDisabled |= FLAG_DISABLED_BY_PUBLISHER;
+ }
LauncherAppState launcherAppState = LauncherAppState.getInstance();
Drawable unbadgedIcon = launcherAppState.getShortcutManager()
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 4ed2467..341c7c8 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -230,6 +230,8 @@
public static final int QSB_ALPHA_INDEX_STATE_CHANGE = 0;
public static final int QSB_ALPHA_INDEX_Y_TRANSLATION = 1;
public static final int QSB_ALPHA_INDEX_PAGE_SCROLL = 2;
+ public static final int QSB_ALPHA_INDEX_OVERLAY_SCROLL = 3;
+
MultiStateAlphaController mQsbAlphaController;
@@ -482,7 +484,7 @@
public void initParentViews(View parent) {
super.initParentViews(parent);
mPageIndicator.setAccessibilityDelegate(new OverviewAccessibilityDelegate());
- mQsbAlphaController = new MultiStateAlphaController(mLauncher.getQsbContainer(), 3);
+ mQsbAlphaController = new MultiStateAlphaController(mLauncher.getQsbContainer(), 4);
}
private int getDefaultPage() {
@@ -1465,6 +1467,8 @@
setWorkspaceTranslationAndAlpha(Direction.X, transX, alpha);
setHotseatTranslationAndAlpha(Direction.X, transX, alpha);
onWorkspaceOverallScrollChanged();
+
+ mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_OVERLAY_SCROLL);
}
/**
@@ -2308,12 +2312,13 @@
+ "View: " + child + " tag: " + child.getTag();
throw new IllegalStateException(msg);
}
- beginDragShared(child, new Point(), source, accessible,
- (ItemInfo) dragObject, new DragPreviewProvider(child));
+ beginDragShared(child, source, accessible, (ItemInfo) dragObject,
+ new DragPreviewProvider(child));
}
- public void beginDragShared(View child, Point relativeTouchPos, DragSource source,
- boolean accessible, ItemInfo dragObject, DragPreviewProvider previewProvider) {
+
+ public DragView beginDragShared(View child, DragSource source, boolean accessible,
+ ItemInfo dragObject, DragPreviewProvider previewProvider) {
child.clearFocus();
child.setPressed(false);
@@ -2325,34 +2330,19 @@
final Bitmap b = previewProvider.createDragBitmap(mCanvas);
int halfPadding = previewProvider.previewPadding / 2;
- final int bmpWidth = b.getWidth();
- final int bmpHeight = b.getHeight();
-
- float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
- int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
- int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2 - halfPadding);
+ float scale = previewProvider.getScaleAndPosition(b, mTempXY);
+ int dragLayerX = mTempXY[0];
+ int dragLayerY = mTempXY[1];
DeviceProfile grid = mLauncher.getDeviceProfile();
Point dragVisualizeOffset = null;
Rect dragRect = null;
if (child instanceof BubbleTextView) {
- BubbleTextView icon = (BubbleTextView) child;
int iconSize = grid.iconSizePx;
int top = child.getPaddingTop();
- int left = (bmpWidth - iconSize) / 2;
+ int left = (b.getWidth() - iconSize) / 2;
int right = left + iconSize;
int bottom = top + iconSize;
- if (icon.isLayoutHorizontal()) {
- // If the layout is horizontal, then if we are just picking up the icon, then just
- // use the child position since the icon is top-left aligned. Otherwise, offset
- // the drag layer position horizontally so that the icon is under the current
- // touch position.
- if (icon.getIcon().getBounds().contains(relativeTouchPos.x, relativeTouchPos.y)) {
- dragLayerX = Math.round(mTempXY[0]);
- } else {
- dragLayerX = Math.round(mTempXY[0] + relativeTouchPos.x - (bmpWidth / 2));
- }
- }
dragLayerY += top;
// Note: The drag region is used to calculate drag layer offsets, but the
// dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
@@ -2384,6 +2374,7 @@
if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
mLauncher.enterSpringLoadedDragMode();
}
+ return dv;
}
public boolean transitionStateShouldAllowDrop() {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 428f784..d860189 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -348,9 +348,10 @@
mAppsRecyclerView.preMeasureViews(mAdapter);
mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
- // TODO(hyunyoungs): clean up setting the content and the reveal view.
if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
getRevealView().setVisibility(View.VISIBLE);
+ getContentView().setVisibility(View.VISIBLE);
+ getContentView().setBackground(null);
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index 7746245..b965d74 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -140,10 +140,11 @@
* Resets the search bar state.
*/
public void reset() {
- mQuery = null;
unfocusSearchField();
mCb.clearSearchResult();
mInput.setText("");
+ // We need to reset this after we clear the input text
+ mQuery = null;
hideKeyboard();
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index df209b5..40244b2 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -12,7 +12,6 @@
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
@@ -60,6 +59,8 @@
private ObjectAnimator mCaretAnimator;
private final long mCaretAnimationDuration;
private final Interpolator mCaretInterpolator;
+ private CaretDrawable mCaretDrawable;
+ private float mLastCaretProgress;
private float mStatusBarHeight;
@@ -77,6 +78,8 @@
private float mShiftRange; // changes depending on the orientation
private float mProgress; // [0, 1], mShiftRange * mProgress = shiftCurrent
+ private float mVelocityForCaret;
+
private static final float DEFAULT_SHIFT_RANGE = 10;
private static final float RECATCH_REJECTION_FRACTION = .0875f;
@@ -204,8 +207,12 @@
if (mAppsView == null) {
return false; // early termination.
}
+
+ mVelocityForCaret = velocity;
+
float shift = Math.min(Math.max(0, mShiftStart + displacement), mShiftRange);
setProgress(shift / mShiftRange);
+
return true;
}
@@ -225,7 +232,9 @@
LauncherLogProto.Action.UP,
LauncherLogProto.HOTSEAT);
}
- mLauncher.showAppsView(true, true, false, false);
+ mLauncher.showAppsView(true /* animated */,
+ false /* updatePredictedApps */,
+ false /* focusSearchBar */);
} else {
calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
mLauncher.showWorkspace(true);
@@ -243,7 +252,9 @@
LauncherLogProto.Action.UP,
LauncherLogProto.HOTSEAT);
}
- mLauncher.showAppsView(true, true, false, false);
+ mLauncher.showAppsView(true, /* animated */
+ false /* updatePredictedApps */,
+ false /* focusSearchBar */);
}
}
}
@@ -260,15 +271,11 @@
// Initialize values that should not change until #onDragEnd
mStatusBarHeight = mLauncher.getDragLayer().getInsets().top;
mHotseat.setVisibility(View.VISIBLE);
- mHotseat.bringToFront();
if (!mLauncher.isAllAppsVisible()) {
mLauncher.tryAndUpdatePredictedApps();
mHotseatBackgroundColor = mHotseat.getBackgroundDrawableColor();
mHotseat.setBackgroundTransparent(true /* transparent */);
mAppsView.setVisibility(View.VISIBLE);
- mAppsView.getContentView().setVisibility(View.VISIBLE);
- mAppsView.getContentView().setBackground(null);
- mAppsView.getRevealView().setVisibility(View.VISIBLE);
mAppsView.setRevealDrawableColor(mHotseatBackgroundColor);
}
}
@@ -330,6 +337,7 @@
mWorkspace.setWorkspaceYTranslationAndAlpha(
PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent),
interpolation);
+ updateCaret(progress);
updateLightStatusBar(shiftCurrent);
}
@@ -353,6 +361,7 @@
return;
}
if (mDetector.isIdleState()) {
+ mVelocityForCaret = -VerticalPullDetector.RELEASE_VELOCITY_PX_MS;
preparePull(true);
mAnimationDuration = duration;
mShiftStart = mAppsView.getTranslationY();
@@ -405,7 +414,7 @@
@Override
public void onAnimationEnd(Animator animator) {
- finishPullDown(false);
+ finishPullDown();
mDiscoBounceAnimation = null;
mIsTranslateWithoutWorkspace = false;
}
@@ -425,6 +434,7 @@
}
Interpolator interpolator;
if (mDetector.isIdleState()) {
+ mVelocityForCaret = VerticalPullDetector.RELEASE_VELOCITY_PX_MS;
preparePull(true);
mAnimationDuration = duration;
mShiftStart = mAppsView.getTranslationY();
@@ -453,7 +463,7 @@
if (canceled) {
return;
} else {
- finishPullDown(true);
+ finishPullDown();
cleanUpAnimation();
mDetector.finishedScrolling();
}
@@ -465,21 +475,14 @@
public void finishPullUp() {
mHotseat.setVisibility(View.INVISIBLE);
setProgress(0f);
- animateCaret();
}
- public void finishPullDown(boolean animated) {
+ public void finishPullDown() {
mAppsView.setVisibility(View.INVISIBLE);
mHotseat.setBackgroundTransparent(false /* transparent */);
mHotseat.setVisibility(View.VISIBLE);
mAppsView.reset();
setProgress(1f);
- if (animated) {
- animateCaret();
- } else {
- mWorkspace.getPageIndicator().getCaretDrawable()
- .setLevel(CaretDrawable.LEVEL_CARET_POINTING_UP);
- }
}
private void cancelAnimation() {
@@ -502,17 +505,41 @@
mCurrentAnimation = null;
}
- private void animateCaret() {
+ private void updateCaret(float shift) {
+ // Animate to a neutral state by default
+ float newCaretProgress = CaretDrawable.PROGRESS_CARET_NEUTRAL;
+
+ // If we're in portrait and the shift is not 0 or 1, adjust the caret based on velocity
+ if (0f < shift && shift < 1f && !mLauncher.useVerticalBarLayout()) {
+ // How fast are we moving as a percentage of the minimum fling velocity?
+ final float pctOfFlingVelocity = Math.max(-1, Math.min(
+ mVelocityForCaret / VerticalPullDetector.RELEASE_VELOCITY_PX_MS, 1));
+
+ mCaretDrawable.setCaretProgress(pctOfFlingVelocity);
+
+ // Set the last caret progress to this progress to prevent animator cancellation
+ mLastCaretProgress = pctOfFlingVelocity;
+ } else if (!mDetector.isDraggingState()) {
+ // Otherwise, if we're not dragging, match the caret to the appropriate state
+ if (Float.compare(shift, 0f) == 0) { // All Apps is up
+ newCaretProgress = CaretDrawable.PROGRESS_CARET_POINTING_DOWN;
+ } else if (Float.compare(shift, 1f) == 0) { // All Apps is down
+ newCaretProgress = CaretDrawable.PROGRESS_CARET_POINTING_UP;
+ }
+ }
+
+ // If the new progress is the same as the last progress we animated to, terminate early
+ if (Float.compare(mLastCaretProgress, newCaretProgress) == 0) {
+ return;
+ }
+
if (mCaretAnimator.isRunning()) {
- mCaretAnimator.cancel(); // stop the animator in its tracks
+ mCaretAnimator.cancel(); // Stop the animator in its tracks
}
- if (mLauncher.isAllAppsVisible()) {
- mCaretAnimator.setIntValues(CaretDrawable.LEVEL_CARET_POINTING_DOWN);
- } else {
- mCaretAnimator.setIntValues(CaretDrawable.LEVEL_CARET_POINTING_UP);
- }
-
+ // Update the progress and start the animation
+ mLastCaretProgress = newCaretProgress;
+ mCaretAnimator.setFloatValues(newCaretProgress);
mCaretAnimator.start();
}
@@ -520,11 +547,14 @@
mAppsView = appsView;
mHotseat = hotseat;
mWorkspace = workspace;
- mCaretAnimator = ObjectAnimator.ofInt(mWorkspace.getPageIndicator().getCaretDrawable(),
- "level", CaretDrawable.LEVEL_CARET_POINTING_UP); // we will set values later
+ mCaretDrawable = mWorkspace.getPageIndicator().getCaretDrawable();
+ mHotseat.addOnLayoutChangeListener(this);
+ mHotseat.bringToFront();
+
+ // we will set values later
+ mCaretAnimator = ObjectAnimator.ofFloat(mCaretDrawable, "caretProgress", 0);
mCaretAnimator.setDuration(mCaretAnimationDuration);
mCaretAnimator.setInterpolator(mCaretInterpolator);
- mHotseat.addOnLayoutChangeListener(this);
}
@Override
@@ -537,6 +567,4 @@
}
setProgress(mProgress);
}
-
-
}
diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/allapps/VerticalPullDetector.java
index 8bb845a..6eab1c0 100644
--- a/src/com/android/launcher3/allapps/VerticalPullDetector.java
+++ b/src/com/android/launcher3/allapps/VerticalPullDetector.java
@@ -26,7 +26,7 @@
/**
* The minimum release velocity in pixels per millisecond that triggers fling..
*/
- private static final float RELEASE_VELOCITY_PX_MS = 1.0f;
+ public static final float RELEASE_VELOCITY_PX_MS = 1.0f;
/**
* The time constant used to calculate dampening in the low-pass filter of scroll velocity.
@@ -87,6 +87,10 @@
return mState == ScrollState.SETTLING;
}
+ public boolean isDraggingState() {
+ return mState == ScrollState.DRAGGING;
+ }
+
private float mDownX;
private float mDownY;
private float mDownMillis;
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index e95f07b..8a2ae94 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -47,7 +47,8 @@
import java.util.Arrays;
public class DragView extends View {
- public static int COLOR_CHANGE_DURATION = 120;
+ public static final int COLOR_CHANGE_DURATION = 120;
+ public static final int VIEW_ZOOM_DURATION = 150;
@Thunk static float sDragAlpha = 1f;
@@ -73,6 +74,11 @@
@Thunk float[] mCurrentFilter;
private ValueAnimator mFilterAnimator;
+ private int mLastTouchX;
+ private int mLastTouchY;
+ private int mAnimatedShiftX;
+ private int mAnimatedShiftY;
+
/**
* Construct the drag view.
* <p>
@@ -98,7 +104,7 @@
// Animate the view into the correct position
mAnim = LauncherAnimUtils.ofFloat(this, 0f, 1f);
- mAnim.setDuration(150);
+ mAnim.setDuration(VIEW_ZOOM_DURATION);
mAnim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
@@ -310,7 +316,6 @@
/**
* Create a window containing this view and show it.
*
- * @param windowToken obtained from v.getWindowToken() from one of your views
* @param touchX the x coordinate the user touched in DragLayer coordinates
* @param touchY the y coordinate the user touched in DragLayer coordinates
*/
@@ -323,8 +328,7 @@
lp.height = mBitmap.getHeight();
lp.customPosition = true;
setLayoutParams(lp);
- setTranslationX(touchX - mRegistrationX);
- setTranslationY(touchY - mRegistrationY);
+ move(touchX, touchY);
// Post the animation to skip other expensive work happening on the first frame
post(new Runnable() {
public void run() {
@@ -347,8 +351,32 @@
* @param touchY the y coordinate the user touched in DragLayer coordinates
*/
public void move(int touchX, int touchY) {
- setTranslationX(touchX - mRegistrationX);
- setTranslationY(touchY - mRegistrationY);
+ mLastTouchX = touchX;
+ mLastTouchY = touchY;
+ applyTranslation();
+ }
+
+ public void animateShift(final int shiftX, final int shiftY) {
+ if (mAnim.isStarted()) {
+ return;
+ }
+ mAnimatedShiftX = shiftX;
+ mAnimatedShiftY = shiftY;
+ applyTranslation();
+ mAnim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float fraction = 1 - animation.getAnimatedFraction();
+ mAnimatedShiftX = (int) (fraction * shiftX);
+ mAnimatedShiftY = (int) (fraction * shiftY);
+ applyTranslation();
+ }
+ });
+ }
+
+ private void applyTranslation() {
+ setTranslationX(mLastTouchX - mRegistrationX + mAnimatedShiftX);
+ setTranslationY(mLastTouchY - mRegistrationY + mAnimatedShiftY);
}
public void remove() {
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index a00bc92..b90c2fd 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -25,13 +25,12 @@
import android.widget.TextView;
import com.android.launcher3.HolographicOutlineHelper;
+import com.android.launcher3.Launcher;
import com.android.launcher3.PreloadIconDrawable;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.folder.FolderIcon;
-import java.util.concurrent.atomic.AtomicInteger;
-
/**
* A utility class to generate preview bitmap for dragging.
*/
@@ -59,9 +58,7 @@
}
/**
- * Draw the View v into the given Canvas.
- *
- * @param destCanvas the canvas to draw on
+ * Draws the {@link #mView} into the given {@param destCanvas}.
*/
private void drawDragView(Canvas destCanvas) {
destCanvas.save();
@@ -98,7 +95,7 @@
}
/**
- * Returns a new bitmap to show when the given View is being dragged around.
+ * Returns a new bitmap to show when the {@link #mView} is being dragged around.
* Responsibility for the bitmap is transferred to the caller.
*/
public Bitmap createDragBitmap(Canvas canvas) {
@@ -152,4 +149,12 @@
}
return bounds;
}
+
+ public float getScaleAndPosition(Bitmap preview, int[] outPos) {
+ float scale = Launcher.getLauncher(mView.getContext())
+ .getDragLayer().getLocationInDragLayer(mView, outPos);
+ outPos[0] = Math.round(outPos[0] - (preview.getWidth() - scale * mView.getWidth()) / 2);
+ outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getHeight() / 2 - previewPadding / 2);
+ return scale;
+ }
}
diff --git a/src/com/android/launcher3/graphics/ScaledPreviewProvider.java b/src/com/android/launcher3/graphics/ScaledPreviewProvider.java
deleted file mode 100644
index a7d121b..0000000
--- a/src/com/android/launcher3/graphics/ScaledPreviewProvider.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.graphics;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.launcher3.HolographicOutlineHelper;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.graphics.DragPreviewProvider;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size
- */
-public class ScaledPreviewProvider extends DragPreviewProvider {
-
- public ScaledPreviewProvider(View v) {
- super(v);
- }
-
- @Override
- public Bitmap createDragOutline(Canvas canvas) {
- if (mView instanceof TextView) {
- Bitmap b = drawScaledPreview(canvas);
-
- final int outlineColor = mView.getResources().getColor(R.color.outline_color);
- HolographicOutlineHelper.obtain(mView.getContext())
- .applyExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
- canvas.setBitmap(null);
- return b;
- }
- return super.createDragOutline(canvas);
- }
-
- @Override
- public Bitmap createDragBitmap(Canvas canvas) {
- if (mView instanceof TextView) {
- Bitmap b = drawScaledPreview(canvas);
- canvas.setBitmap(null);
- return b;
-
- } else {
- return super.createDragBitmap(canvas);
- }
- }
-
- private Bitmap drawScaledPreview(Canvas canvas) {
- Drawable d = Workspace.getTextViewIcon((TextView) mView);
- Rect bounds = getDrawableBounds(d);
-
- int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
-
- final Bitmap b = Bitmap.createBitmap(
- size + DRAG_BITMAP_PADDING,
- size + DRAG_BITMAP_PADDING,
- Bitmap.Config.ARGB_8888);
-
- canvas.setBitmap(b);
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.translate(DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2);
- canvas.scale(((float) size) / bounds.width(), ((float) size) / bounds.height(), 0, 0);
- canvas.translate(bounds.left, bounds.top);
- d.draw(canvas);
- canvas.restore();
- return b;
- }
-}
diff --git a/src/com/android/launcher3/pageindicators/CaretDrawable.java b/src/com/android/launcher3/pageindicators/CaretDrawable.java
index fcf3d23..1451773 100644
--- a/src/com/android/launcher3/pageindicators/CaretDrawable.java
+++ b/src/com/android/launcher3/pageindicators/CaretDrawable.java
@@ -28,11 +28,11 @@
import android.graphics.drawable.Drawable;
public class CaretDrawable extends Drawable {
- public static final int LEVEL_CARET_POINTING_UP = 0; // minimum possible level value
- public static final int LEVEL_CARET_POINTING_DOWN = 10000; // maximum possible level value
- public static final int LEVEL_CARET_NEUTRAL = LEVEL_CARET_POINTING_DOWN / 2;
+ public static final float PROGRESS_CARET_POINTING_UP = -1f;
+ public static final float PROGRESS_CARET_POINTING_DOWN = 1f;
+ public static final float PROGRESS_CARET_NEUTRAL = 0;
- private float mCaretProgress;
+ private float mCaretProgress = PROGRESS_CARET_NEUTRAL;
private Paint mShadowPaint = new Paint();
private Paint mCaretPaint = new Paint();
@@ -72,23 +72,48 @@
final float left = getBounds().left + (mShadowPaint.getStrokeWidth() / 2);
final float top = getBounds().top + (mShadowPaint.getStrokeWidth() / 2);
+ // When the bounds are square, this will result in a caret with a right angle
final float verticalInset = (height / 4);
final float caretHeight = (height - (verticalInset * 2));
mPath.reset();
- mPath.moveTo(left, top + caretHeight * (1 - mCaretProgress));
- mPath.lineTo(left + (width / 2), top + caretHeight * mCaretProgress);
- mPath.lineTo(left + width, top + caretHeight * (1 - mCaretProgress));
+ mPath.moveTo(left, top + caretHeight * (1 - getNormalizedCaretProgress()));
+ mPath.lineTo(left + (width / 2), top + caretHeight * getNormalizedCaretProgress());
+ mPath.lineTo(left + width, top + caretHeight * (1 - getNormalizedCaretProgress()));
canvas.drawPath(mPath, mShadowPaint);
canvas.drawPath(mPath, mCaretPaint);
}
- @Override
- protected boolean onLevelChange(int level) {
- mCaretProgress = (float) level / (float) LEVEL_CARET_POINTING_DOWN;
+ /**
+ * Sets the caret progress
+ *
+ * @param progress The progress ({@value #PROGRESS_CARET_POINTING_UP} for pointing up,
+ * {@value #PROGRESS_CARET_POINTING_DOWN} for pointing down, {@value #PROGRESS_CARET_NEUTRAL}
+ * for neutral)
+ */
+ public void setCaretProgress(float progress) {
+ mCaretProgress = progress;
invalidateSelf();
- return true;
+ }
+
+ /**
+ * Returns the caret progress
+ *
+ * @return The progress
+ */
+ public float getCaretProgress() {
+ return mCaretProgress;
+ }
+
+ /**
+ * Returns the caret progress normalized to [0..1]
+ *
+ * @return The normalized progress
+ */
+ public float getNormalizedCaretProgress() {
+ return (mCaretProgress - PROGRESS_CARET_POINTING_UP) /
+ (PROGRESS_CARET_POINTING_DOWN - PROGRESS_CARET_POINTING_UP);
}
@Override
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
index c48d160..450d36d 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -21,7 +21,6 @@
import android.util.AttributeSet;
import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.R;
/**
* A {@link BubbleTextView} that has the shortcut icon on the left and drag handle on the right.
@@ -41,10 +40,7 @@
}
@Override
- /** Use the BubbleTextView icon for the start and the drag handle for the end. */
protected void applyCompoundDrawables(Drawable icon) {
- Drawable dragHandle = getResources().getDrawable(R.drawable.deep_shortcuts_drag_handle);
- dragHandle.setBounds(0, 0, dragHandle.getIntrinsicWidth(), dragHandle.getIntrinsicHeight());
- setCompoundDrawablesRelative(icon, null, dragHandle, null);
+ // The icon is drawn in a separate view.
}
}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 7cb2d43..b651f25 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -17,21 +17,20 @@
package com.android.launcher3.shortcuts;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.content.Context;
-import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.view.animation.DecelerateInterpolator;
+import android.view.View;
import android.widget.FrameLayout;
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.LauncherViewPropertyAnimator;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.PillRevealOutlineProvider;
+import com.android.launcher3.util.PillWidthRevealOutlineProvider;
/**
* A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
@@ -39,10 +38,12 @@
*/
public class DeepShortcutView extends FrameLayout {
- private int mRadius;
- private Rect mPillRect;
+ private static final Point sTempPoint = new Point();
- private BubbleTextView mBubbleText;
+ private final Rect mPillRect;
+
+ private DeepShortcutTextView mBubbleText;
+ private View mIconView;
public DeepShortcutView(Context context) {
this(context, null, 0);
@@ -55,70 +56,116 @@
public DeepShortcutView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mRadius = getResources().getDimensionPixelSize(R.dimen.bg_pill_radius);
mPillRect = new Rect();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mBubbleText = (BubbleTextView) findViewById(R.id.deep_shortcut);
+ mIconView = findViewById(R.id.deep_shortcut_icon);
+ mBubbleText = (DeepShortcutTextView) findViewById(R.id.deep_shortcut);
}
- public BubbleTextView getBubbleText() {
+ public DeepShortcutTextView getBubbleText() {
return mBubbleText;
}
+ public void setWillDrawIcon(boolean willDraw) {
+ mIconView.setVisibility(willDraw ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ public boolean willDrawIcon() {
+ return mIconView.getVisibility() == View.VISIBLE;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
- @Override
- public void setPivotX(float pivotX) {
- super.setPivotX(pivotX);
- mBubbleText.setPivotX(pivotX);
+ public void applyShortcutInfo(ShortcutInfo info) {
+ IconCache cache = LauncherAppState.getInstance().getIconCache();
+ mBubbleText.applyFromShortcutInfo(info, cache);
+ mIconView.setBackground(mBubbleText.getIcon());
}
- @Override
- public void setPivotY(float pivotY) {
- super.setPivotY(pivotY);
- mBubbleText.setPivotY(pivotY);
+ public View getIconView() {
+ return mIconView;
}
/**
- * Creates an animator to play when the shortcut container is being opened.
+ * Creates an animator to play when the shortcut container is being opened or closed.
*/
- public Animator createOpenAnimation(long animationDelay, boolean isContainerAboveIcon) {
- final Resources res = getResources();
- setVisibility(INVISIBLE);
+ public Animator createOpenCloseAnimation(
+ boolean isContainerAboveIcon, boolean pivotLeft, boolean isReverse) {
+ Point center = getIconCenter();
+ return new ZoomRevealOutlineProvider(center.x, center.y, mPillRect,
+ this, mIconView, isContainerAboveIcon, pivotLeft)
+ .createRevealAnimator(this, isReverse);
+ }
- AnimatorSet openAnimation = LauncherAnimUtils.createAnimatorSet();
+ /**
+ * Creates an animator which clips the container to form a circle around the icon.
+ */
+ public Animator collapseToIcon() {
+ int halfHeight = getMeasuredHeight() / 2;
+ int iconCenterX = getIconCenter().x;
+ return new PillWidthRevealOutlineProvider(mPillRect,
+ iconCenterX - halfHeight, iconCenterX + halfHeight)
+ .createRevealAnimator(this, true);
+ }
- Animator reveal = new PillRevealOutlineProvider((int) getPivotX(), (int) getPivotY(),
- mPillRect, mRadius).createRevealAnimator(this);
- reveal.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- setVisibility(VISIBLE);
- }
- });
+ /**
+ * Returns the position of the center of the icon relative to the container.
+ */
+ public Point getIconCenter() {
+ sTempPoint.y = sTempPoint.x = getMeasuredHeight() / 2;
+ if (Utilities.isRtl(getResources())) {
+ sTempPoint.x = getMeasuredWidth() - sTempPoint.x;
+ }
+ return sTempPoint;
+ }
- float transY = res.getDimensionPixelSize(R.dimen.deep_shortcut_anim_translation_y);
- Animator translationY = ObjectAnimator.ofFloat(this, TRANSLATION_Y,
- isContainerAboveIcon ? transY : -transY, 0);
+ /**
+ * Extension of {@link PillRevealOutlineProvider} which scales the icon based on the height.
+ */
+ private static class ZoomRevealOutlineProvider extends PillRevealOutlineProvider {
- // Only scale mBubbleText (the icon and text, not the background).
- mBubbleText.setScaleX(0);
- mBubbleText.setScaleY(0);
- LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(mBubbleText)
- .scaleX(1).scaleY(1);
+ private final View mTranslateView;
+ private final View mZoomView;
- openAnimation.playTogether(reveal, translationY, scale);
- openAnimation.setStartDelay(animationDelay);
- openAnimation.setDuration(res.getInteger(R.integer.config_deepShortcutOpenDuration));
- openAnimation.setInterpolator(new DecelerateInterpolator());
- return openAnimation;
+ private final float mFullHeight;
+ private final float mTranslateYMultiplier;
+
+ private final boolean mPivotLeft;
+ private final float mTranslateX;
+
+ public ZoomRevealOutlineProvider(int x, int y, Rect pillRect,
+ View translateView, View zoomView, boolean isContainerAboveIcon, boolean pivotLeft) {
+ super(x, y, pillRect);
+ mTranslateView = translateView;
+ mZoomView = zoomView;
+ mFullHeight = pillRect.height();
+
+ mTranslateYMultiplier = isContainerAboveIcon ? 0.5f : -0.5f;
+
+ mPivotLeft = pivotLeft;
+ mTranslateX = pivotLeft ? pillRect.height() / 2 : pillRect.right - pillRect.height() / 2;
+ }
+
+ @Override
+ public void setProgress(float progress) {
+ super.setProgress(progress);
+
+ mZoomView.setScaleX(progress);
+ mZoomView.setScaleY(progress);
+
+ float height = mOutline.height();
+ mTranslateView.setTranslationY(mTranslateYMultiplier * (mFullHeight - height));
+
+ float pivotX = mPivotLeft ? (mOutline.left + height / 2) : (mOutline.right - height / 2);
+ mTranslateView.setTranslationX(mTranslateX - pivotX);
+ }
}
}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 73d8569..7aa2123 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -17,6 +17,7 @@
package com.android.launcher3.shortcuts;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.content.ComponentName;
@@ -39,8 +40,10 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
@@ -50,6 +53,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherViewPropertyAnimator;
+import com.android.launcher3.LogAccelerateInterpolator;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
@@ -59,7 +63,6 @@
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.graphics.ScaledPreviewProvider;
import com.android.launcher3.graphics.TriangleShape;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -78,6 +81,8 @@
UserEventDispatcher.LaunchSourceProvider {
private static final String TAG = "ShortcutsContainer";
+ private final Point mIconShift = new Point();
+
private final Launcher mLauncher;
private final DeepShortcutManager mDeepShortcutsManager;
private final int mDragDeadzone;
@@ -96,8 +101,12 @@
private boolean mIsLeftAligned;
private boolean mIsAboveIcon;
private View mArrow;
+
+ private Animator mOpenCloseAnimator;
+ private boolean mDeferContainerRemoval;
+ private boolean mIsOpen;
+
private boolean mSrcIconDragStarted;
- private LauncherViewPropertyAnimator mArrowHoverAnimator;
private boolean mIsRtl;
private int mArrowHorizontalOffset;
@@ -167,7 +176,6 @@
final int arrowVerticalOffset = resources.getDimensionPixelSize(
R.dimen.deep_shortcuts_arrow_vertical_offset);
mArrow = addArrowView(mArrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
- mArrowHoverAnimator = new LauncherViewPropertyAnimator(mArrow);
animateOpen();
@@ -218,9 +226,9 @@
@Override
public void run() {
+ DeepShortcutView shortcutViewContainer = getShortcutAt(mShortcutChildIndex);
+ shortcutViewContainer.applyShortcutInfo(mShortcutChildInfo);
BubbleTextView shortcutView = getShortcutAt(mShortcutChildIndex).getBubbleText();
- shortcutView.applyFromShortcutInfo(mShortcutChildInfo,
- LauncherAppState.getInstance().getIconCache());
// Use the long label as long as it exists and fits.
int availableWidth = shortcutView.getWidth() - shortcutView.getTotalPaddingLeft()
- shortcutView.getTotalPaddingRight();
@@ -248,30 +256,54 @@
private void animateOpen() {
setVisibility(View.VISIBLE);
+ mIsOpen = true;
final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
final int shortcutCount = getShortcutCount();
- final int pivotX = mIsLeftAligned ? mArrowHorizontalOffset
- : getMeasuredWidth() - mArrowHorizontalOffset;
- final int pivotY = getShortcutAt(0).getMeasuredHeight() / 2;
+
+ final long duration = getResources().getInteger(
+ R.integer.config_deepShortcutOpenDuration);
+ final long stagger = getResources().getInteger(
+ R.integer.config_deepShortcutOpenStagger);
+
+ // Animate shortcuts
+ DecelerateInterpolator interpolator = new DecelerateInterpolator();
for (int i = 0; i < shortcutCount; i++) {
- DeepShortcutView deepShortcutView = getShortcutAt(i);
- deepShortcutView.setPivotX(pivotX);
- deepShortcutView.setPivotY(pivotY);
+ final DeepShortcutView deepShortcutView = getShortcutAt(i);
+ deepShortcutView.setVisibility(INVISIBLE);
+
+ Animator anim = deepShortcutView.createOpenCloseAnimation(
+ mIsAboveIcon, mIsLeftAligned, false);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ deepShortcutView.setVisibility(VISIBLE);
+ }
+ });
+ anim.setDuration(duration);
int animationIndex = mIsAboveIcon ? shortcutCount - i - 1 : i;
- long animationDelay = animationIndex * getResources().getInteger(
- R.integer.config_deepShortcutOpenStagger);
- shortcutAnims.play(deepShortcutView.createOpenAnimation(animationDelay, mIsAboveIcon));
+ anim.setStartDelay(stagger * animationIndex);
+ anim.setInterpolator(interpolator);
+ shortcutAnims.play(anim);
}
+ shortcutAnims.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator = null;
+ }
+ });
+
+ // Animate the arrow
mArrow.setScaleX(0);
mArrow.setScaleY(0);
- final long shortcutAnimDuration = shortcutAnims.getChildAnimations().get(0).getDuration();
- final long arrowScaleDelay = shortcutAnimDuration / 6;
- final long arrowScaleDuration = shortcutAnimDuration - arrowScaleDelay;
+ final long arrowScaleDelay = duration / 6;
+ final long arrowScaleDuration = duration - arrowScaleDelay;
Animator arrowScale = new LauncherViewPropertyAnimator(mArrow).scaleX(1).scaleY(1);
arrowScale.setStartDelay(arrowScaleDelay);
arrowScale.setDuration(arrowScaleDuration);
shortcutAnims.play(arrowScale);
+
+ mOpenCloseAnimator = shortcutAnims;
shortcutAnims.start();
}
@@ -432,7 +464,6 @@
Utilities.translateEventCoordinates(this, mLauncher.getDragLayer(), ev);
final int dragLayerX = (int) ev.getX();
final int dragLayerY = (int) ev.getY();
- int shortcutCount = getShortcutCount();
if (action == MotionEvent.ACTION_MOVE) {
if (mLastX != 0 || mLastY != 0) {
mDistanceDragged += Math.hypot(mLastX - x, mLastY - y);
@@ -441,8 +472,6 @@
mLastY = y;
if (shouldStartDeferredDrag((int) x, (int) y)) {
- DeepShortcutView topShortcut = getShortcutAt(0);
- DeepShortcutView bottomShortcut = getShortcutAt(shortcutCount - 1);
mSrcIconDragStarted = true;
cleanupDeferredDrag(true);
mDeferredDragIcon.getParent().requestDisallowInterceptTouchEvent(false);
@@ -480,7 +509,7 @@
return distFromTouchDown > mStartDragThreshold;
}
- public void cleanupDeferredDrag(boolean updateSrcVisibility) {
+ private void cleanupDeferredDrag(boolean updateSrcVisibility) {
if (mDragView != null) {
mDragView.remove();
}
@@ -502,8 +531,8 @@
}
public boolean onLongClick(View v) {
- // Return early if this is not initiated from a touch
- if (!v.isInTouchMode()) return false;
+ // Return early if this is not initiated from a touch or not the correct view
+ if (!v.isInTouchMode() || !(v.getParent() instanceof DeepShortcutView)) return false;
// Return if global dragging is not enabled
if (!mLauncher.isDraggingEnabled()) return false;
@@ -514,8 +543,20 @@
mLauncher.getModel().updateShortcutInfo(unbadgedInfo.mDetail, badged);
// Long clicked on a shortcut.
- mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false, badged,
- new ScaledPreviewProvider(v));
+
+ mDeferContainerRemoval = true;
+ DeepShortcutView sv = (DeepShortcutView) v.getParent();
+ sv.setWillDrawIcon(false);
+
+ // Move the icon to align with the center-top of the touch point
+ mIconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
+ mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
+
+ DragView dv = mLauncher.getWorkspace().beginDragShared(
+ sv.getBubbleText(), this, false, badged,
+ new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift));
+ dv.animateShift(-mIconShift.x, -mIconShift.y);
+
// TODO: support dragging from within folder without having to close it
mLauncher.closeFolder();
return false;
@@ -560,13 +601,25 @@
public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
// Either the original icon or one of the shortcuts was dragged.
// Hide the container, but don't remove it yet because that interferes with touch events.
- setVisibility(INVISIBLE);
+ animateClose();
}
@Override
public void onDragEnd() {
- // Now remove the container.
- mLauncher.closeShortcutsContainer();
+ if (mIsOpen) {
+ animateClose();
+ } else {
+ if (mOpenCloseAnimator != null) {
+ // Close animation is running.
+ mDeferContainerRemoval = false;
+ } else {
+ // Close animation is not running.
+ if (mDeferContainerRemoval) {
+ mDeferContainerRemoval = false;
+ mLauncher.getDragLayer().removeView(this);
+ }
+ }
+ }
}
@Override
@@ -576,6 +629,106 @@
targetParent.containerType = LauncherLogProto.DEEPSHORTCUTS;
}
+ public void animateClose() {
+ if (!mIsOpen) {
+ return;
+ }
+ if (mOpenCloseAnimator != null) {
+ mOpenCloseAnimator.cancel();
+ }
+ mIsOpen = false;
+ mLauncher.getDragController().removeDragListener(this);
+
+ final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
+ final int numShortcuts = getShortcutCount();
+ final long duration = getResources().getInteger(
+ R.integer.config_deepShortcutCloseDuration);
+ final long stagger = getResources().getInteger(
+ R.integer.config_deepShortcutCloseStagger);
+
+ long arrowDelay = (numShortcuts - 1) * stagger + (duration * 4 / 6);
+ int firstShortcutIndex = mIsAboveIcon ? (numShortcuts - 1) : 0;
+ LogAccelerateInterpolator interpolator = new LogAccelerateInterpolator(100, 0);
+ for (int i = 0; i < numShortcuts; i++) {
+ final DeepShortcutView view = getShortcutAt(i);
+ Animator anim;
+ if (view.willDrawIcon()) {
+ anim = view.createOpenCloseAnimation(mIsAboveIcon, mIsLeftAligned, true);
+ int animationIndex = mIsAboveIcon ? i : numShortcuts - i - 1;
+ anim.setStartDelay(stagger * animationIndex);
+ anim.setDuration(duration);
+ anim.setInterpolator(interpolator);
+ } else {
+ // The view is being dragged. Animate it such that it collapses with the drag view
+ anim = view.collapseToIcon();
+ anim.setDuration(DragView.VIEW_ZOOM_DURATION);
+
+ // Scale and translate the view to follow the drag view.
+ Point iconCenter = view.getIconCenter();
+ view.setPivotX(iconCenter.x);
+ view.setPivotY(iconCenter.y);
+
+ float scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / view.getHeight();
+ LauncherViewPropertyAnimator anim2 = new LauncherViewPropertyAnimator(view)
+ .scaleX(scale)
+ .scaleY(scale)
+ .translationX(mIconShift.x)
+ .translationY(mIconShift.y);
+ anim2.setDuration(DragView.VIEW_ZOOM_DURATION);
+ shortcutAnims.play(anim2);
+
+ if (i == firstShortcutIndex) {
+ arrowDelay = 0;
+ }
+ }
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(INVISIBLE);
+ }
+ });
+ shortcutAnims.play(anim);
+ }
+ Animator arrowAnim = new LauncherViewPropertyAnimator(mArrow)
+ .scaleX(0).scaleY(0).setDuration(duration / 6);
+ arrowAnim.setStartDelay(arrowDelay);
+ shortcutAnims.play(arrowAnim);
+
+ shortcutAnims.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator = null;
+ if (mDeferContainerRemoval) {
+ setVisibility(INVISIBLE);
+ } else {
+ close();
+ }
+ }
+ });
+ mOpenCloseAnimator = shortcutAnims;
+ shortcutAnims.start();
+ }
+
+ /**
+ * Closes the folder without animation.
+ */
+ public void close() {
+ if (mOpenCloseAnimator != null) {
+ mOpenCloseAnimator.cancel();
+ mOpenCloseAnimator = null;
+ }
+ mIsOpen = false;
+ mDeferContainerRemoval = false;
+ // Make the original icon visible in All Apps, but not in Workspace or Folders.
+ cleanupDeferredDrag(mDeferredDragIcon.getTag() instanceof AppInfo);
+ mLauncher.getDragController().removeDragListener(this);
+ mLauncher.getDragLayer().removeView(this);
+ }
+
+ public boolean isOpen() {
+ return mIsOpen;
+ }
+
/**
* Shows the shortcuts container for {@param icon}
* @return the container if shown or null.
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
new file mode 100644
index 0000000..a25e475
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -0,0 +1,103 @@
+/*
+ * 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.shortcuts;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.HolographicOutlineHelper;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.DragPreviewProvider;
+
+/**
+ * Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
+ */
+public class ShortcutDragPreviewProvider extends DragPreviewProvider {
+
+ private final Point mPositionShift;
+
+ public ShortcutDragPreviewProvider(View icon, Point shift) {
+ super(icon);
+ mPositionShift = shift;
+ }
+
+ @Override
+ public Bitmap createDragOutline(Canvas canvas) {
+ Bitmap b = drawScaledPreview(canvas);
+
+ final int outlineColor = mView.getResources().getColor(R.color.outline_color);
+ HolographicOutlineHelper.obtain(mView.getContext())
+ .applyExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
+ canvas.setBitmap(null);
+ return b;
+ }
+
+ @Override
+ public Bitmap createDragBitmap(Canvas canvas) {
+ Bitmap b = drawScaledPreview(canvas);
+ canvas.setBitmap(null);
+ return b;
+ }
+
+ private Bitmap drawScaledPreview(Canvas canvas) {
+ Drawable d = mView.getBackground();
+ Rect bounds = getDrawableBounds(d);
+
+ int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
+
+ final Bitmap b = Bitmap.createBitmap(
+ size + DRAG_BITMAP_PADDING,
+ size + DRAG_BITMAP_PADDING,
+ Bitmap.Config.ARGB_8888);
+
+ canvas.setBitmap(b);
+ canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ canvas.translate(DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2);
+ canvas.scale(((float) size) / bounds.width(), ((float) size) / bounds.height(), 0, 0);
+ canvas.translate(bounds.left, bounds.top);
+ d.draw(canvas);
+ canvas.restore();
+ return b;
+ }
+
+ @Override
+ public float getScaleAndPosition(Bitmap preview, int[] outPos) {
+ Launcher launcher = Launcher.getLauncher(mView.getContext());
+ int iconSize = getDrawableBounds(mView.getBackground()).width();
+ float scale = launcher.getDragLayer().getLocationInDragLayer(mView, outPos);
+
+ int iconLeft = mView.getPaddingStart();
+ if (Utilities.isRtl(mView.getResources())) {
+ iconLeft = mView.getWidth() - iconSize - iconLeft;
+ }
+
+ outPos[0] += Math.round(scale * iconLeft + (scale * iconSize - preview.getWidth()) / 2 +
+ mPositionShift.x);
+ outPos[1] += Math.round((scale * mView.getHeight() - preview.getHeight()) / 2
+ + mPositionShift.y);
+ float size = launcher.getDeviceProfile().iconSizePx;
+ return scale * iconSize / size;
+ }
+}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
index 00553df..d7fcda6 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
@@ -26,7 +26,6 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.util.ComponentKey;
/**
* Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
@@ -101,6 +100,14 @@
return mShortcutInfo.isDeclaredInManifest();
}
+ public boolean isEnabled() {
+ return mShortcutInfo.isEnabled();
+ }
+
+ public boolean isDynamic() {
+ return mShortcutInfo.isDynamic();
+ }
+
public int getRank() {
return mShortcutInfo.getRank();
}
diff --git a/src/com/android/launcher3/util/PillRevealOutlineProvider.java b/src/com/android/launcher3/util/PillRevealOutlineProvider.java
index 09ff9bd..3f1e11a 100644
--- a/src/com/android/launcher3/util/PillRevealOutlineProvider.java
+++ b/src/com/android/launcher3/util/PillRevealOutlineProvider.java
@@ -31,19 +31,18 @@
private int mCenterX;
private int mCenterY;
- private Rect mPillRect;
+ protected Rect mPillRect;
/**
* @param x reveal center x
* @param y reveal center y
* @param pillRect round rect that represents the final pill shape
- * @param pillRectRadius radius of the round rect
*/
- public PillRevealOutlineProvider(int x, int y, Rect pillRect, float pillRectRadius) {
+ public PillRevealOutlineProvider(int x, int y, Rect pillRect) {
mCenterX = x;
mCenterY = y;
mPillRect = pillRect;
- mOutlineRadius = pillRectRadius;
+ mOutlineRadius = pillRect.height() / 2f;
}
@Override
@@ -62,5 +61,6 @@
mOutline.top = Math.max(mPillRect.top, mCenterY - currentSize);
mOutline.right = Math.min(mPillRect.right, mCenterX + currentSize);
mOutline.bottom = Math.min(mPillRect.bottom, mCenterY + currentSize);
+ mOutlineRadius = mOutline.height() / 2;
}
}
diff --git a/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java b/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java
new file mode 100644
index 0000000..89dda3b
--- /dev/null
+++ b/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java
@@ -0,0 +1,41 @@
+/*
+ * 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.util;
+
+import android.graphics.Rect;
+
+/**
+ * Extension of {@link PillRevealOutlineProvider} which only changes the width of the pill.
+ */
+public class PillWidthRevealOutlineProvider extends PillRevealOutlineProvider {
+
+ private final int mStartLeft;
+ private final int mStartRight;
+
+ public PillWidthRevealOutlineProvider(Rect pillRect, int left, int right) {
+ super(0, 0, pillRect);
+ mOutline.set(pillRect);
+ mStartLeft = left;
+ mStartRight = right;
+ }
+
+ @Override
+ public void setProgress(float progress) {
+ mOutline.left = (int) (progress * mPillRect.left + (1 - progress) * mStartLeft);
+ mOutline.right = (int) (progress * mPillRect.right + (1 - progress) * mStartRight);
+ }
+}
diff --git a/src/com/android/launcher3/util/RevealOutlineAnimation.java b/src/com/android/launcher3/util/RevealOutlineAnimation.java
index 4447c3b..cd98882 100644
--- a/src/com/android/launcher3/util/RevealOutlineAnimation.java
+++ b/src/com/android/launcher3/util/RevealOutlineAnimation.java
@@ -29,7 +29,12 @@
abstract void setProgress(float progress);
public ValueAnimator createRevealAnimator(final View revealView) {
- ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+ return createRevealAnimator(revealView, false);
+ }
+
+ public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) {
+ ValueAnimator va =
+ isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
final float elevation = revealView.getElevation();
va.addListener(new AnimatorListenerAdapter() {
@@ -54,7 +59,7 @@
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator arg0) {
- float progress = arg0.getAnimatedFraction();
+ float progress = (Float) arg0.getAnimatedValue();
setProgress(progress);
revealView.invalidateOutline();
if (!Utilities.ATLEAST_LOLLIPOP_MR1) {
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index 01808ba..9bd2882 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -16,6 +16,7 @@
package com.android.launcher3.util;
+import android.util.Log;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewTreeObserver.OnDrawListener;
@@ -39,6 +40,9 @@
private View mAttachedView;
private boolean mCompleted;
+ private boolean mLoadAnimationCompleted;
+ private boolean mFirstDrawCompleted;
+
public ViewOnDrawExecutor(DeferredHandler handler) {
mHandler = handler;
}
@@ -72,19 +76,31 @@
@Override
public void onDraw() {
+ mFirstDrawCompleted = true;
mAttachedView.post(this);
}
+ public void onLoadAnimationCompleted() {
+ mLoadAnimationCompleted = true;
+ if (mAttachedView != null) {
+ mAttachedView.post(this);
+ }
+ }
+
@Override
public void run() {
- for (final Runnable r : mTasks) {
- mHandler.post(r);
+ // Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
+ if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
+ for (final Runnable r : mTasks) {
+ mHandler.post(r);
+ }
+ markCompleted();
}
- markCompleted();
}
public void markCompleted() {
mTasks.clear();
+ mCompleted = true;
if (mAttachedView != null) {
mAttachedView.getViewTreeObserver().removeOnDrawListener(this);
mAttachedView.removeOnAttachStateChangeListener(this);