Merge "Initial implementation of Overview keyboard interactions" into ub-launcher3-master
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index d531a46..4a26494 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -48,6 +48,19 @@
It is set to true so that the activity can be started from command line -->
<activity android:name="com.android.quickstep.RecentsActivity"
android:exported="true" />
+
+ <!-- Content provider to settings search -->
+ <provider
+ android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
+ android:authorities="com.android.launcher3"
+ android:grantUriPermissions="true"
+ android:multiprocess="true"
+ android:permission="android.permission.READ_SEARCH_INDEXABLES"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" />
+ </intent-filter>
+ </provider>
</application>
</manifest>
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
index c416844..b3fc4ed 100644
--- a/quickstep/res/layout/fallback_recents_activity.xml
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -15,15 +15,17 @@
-->
<com.android.quickstep.RecentsRootView
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/drag_layer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.android.quickstep.FallbackRecentsView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/HomeScreenElementTheme"
+ android:id="@+id/overview_panel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
- android:clipToPadding="false" />
+ android:clipToPadding="false"
+ android:theme="@style/HomeScreenElementTheme" />
</com.android.quickstep.RecentsRootView>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 8497191..e61e359 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -34,7 +34,7 @@
<!-- Launcher app transition -->
<dimen name="content_trans_y">25dp</dimen>
<dimen name="workspace_trans_y">80dp</dimen>
- <dimen name="recents_adjacent_trans_x">120dp</dimen>
+ <dimen name="recents_adjacent_trans_x">140dp</dimen>
<dimen name="recents_adjacent_trans_y">80dp</dimen>
<fraction name="recents_adjacent_scale">150%</fraction>
</resources>
diff --git a/quickstep/res/xml/indexable_launcher_prefs.xml b/quickstep/res/xml/indexable_launcher_prefs.xml
new file mode 100644
index 0000000..2655402
--- /dev/null
+++ b/quickstep/res/xml/indexable_launcher_prefs.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 Google Inc.
+
+ 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <SwitchPreference
+ android:key="pref_add_icon_to_home"
+ android:title="@string/auto_add_shortcuts_label"
+ android:summary="@string/auto_add_shortcuts_description"
+ android:defaultValue="true"
+ />
+
+ <ListPreference
+ android:key="pref_override_icon_shape"
+ android:title="@string/icon_shape_override_label"
+ android:summary="@string/icon_shape_override_label_location"
+ android:entries="@array/icon_shape_override_paths_names"
+ android:entryValues="@array/icon_shape_override_paths_values"
+ android:defaultValue=""
+ android:persistent="false" />
+
+</PreferenceScreen>
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index ba0cbfa..a7e75bf 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -372,26 +372,24 @@
} else {
// We are launching an adjacent task, so parallax the center and other adjacent task.
TaskView centerTask = (TaskView) recentsView.getPageAt(centerTaskIndex);
- float translationX = Math.abs(v.getTranslationX());
- ObjectAnimator centerTaskParallaxToRight =
+ float translationX = mRecentsTransX / 2;
+ ObjectAnimator centerTaskParallaxOffscreen =
LauncherAnimUtils.ofPropertyValuesHolder(centerTask,
new PropertyListBuilder()
- .scale(v.getScaleX())
.translationX(isRtl ? -translationX : translationX)
.build());
- launcherAnimator.play(centerTaskParallaxToRight);
+ launcherAnimator.play(centerTaskParallaxOffscreen);
int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - launchedTaskIndex);
if (otherAdjacentTaskIndex >= 0
&& otherAdjacentTaskIndex < recentsView.getPageCount()) {
TaskView otherAdjacentTask = (TaskView) recentsView.getPageAt(
otherAdjacentTaskIndex);
- ObjectAnimator otherAdjacentTaskParallaxToRight =
+ ObjectAnimator otherAdjacentTaskParallaxOffscreen =
LauncherAnimUtils.ofPropertyValuesHolder(otherAdjacentTask,
new PropertyListBuilder()
- .translationX(otherAdjacentTask.getTranslationX()
- + (isRtl ? -translationX : translationX))
+ .translationX(isRtl ? -translationX : translationX)
.build());
- launcherAnimator.play(otherAdjacentTaskParallaxToRight);
+ launcherAnimator.play(otherAdjacentTaskParallaxOffscreen);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index 31261d9..efa83e4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -90,6 +90,11 @@
}
@Override
+ public float getOverviewTranslationX(Launcher launcher) {
+ return 0;
+ }
+
+ @Override
public LauncherState getHistoryForState(LauncherState previousState) {
return previousState == OVERVIEW ? OVERVIEW : NORMAL;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index abb4ecf..09acb1d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -57,6 +57,11 @@
}
@Override
+ public float getOverviewTranslationX(Launcher launcher) {
+ return 0;
+ }
+
+ @Override
public void onStateEnabled(Launcher launcher) {
RecentsView rv = launcher.getOverviewPanel();
rv.setOverviewStateEnabled(true);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
index c8b54ad..8b73809 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
@@ -198,8 +198,8 @@
}
} else {
if (goingUp) {
- mPendingAnimation = mRecentsView
- .createTaskDismissAnimation(mTaskBeingDragged, maxDuration);
+ mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
+ true /* animateTaskView */, true /* removeTask */, maxDuration);
mCurrentAnimation = AnimatorPlaybackController
.wrap(mPendingAnimation.anim, maxDuration);
mEndDisplacement = -mTaskBeingDragged.getHeight();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index b7f79b3..b68a3d8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -16,7 +16,6 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -33,7 +32,6 @@
import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
public class RecentsViewStateController implements StateHandler {
@@ -45,8 +43,6 @@
// overall transition while the RecentsView is being shown or hidden.
private final AnimatedFloat mVisibilityMultiplier = new AnimatedFloat(this::onVisibilityProgress);
- private boolean mIsRecentsSlidingInOrOut;
-
public RecentsViewStateController(Launcher launcher) {
mLauncher = launcher;
mRecentsView = launcher.getOverviewPanel();
@@ -59,16 +55,17 @@
if (state.overviewUi) {
mRecentsView.resetTaskVisuals();
}
+ float overviewTranslationX = state.getOverviewTranslationX(mLauncher);
+ int direction = mRecentsView.isRtl() ? -1 : 1;
+ mRecentsView.setTranslationX(overviewTranslationX * direction);
}
@Override
public void setStateWithAnimation(final LauncherState toState,
AnimatorSetBuilder builder, AnimationConfig config) {
- LauncherState fromState = mLauncher.getStateManager().getState();
- mIsRecentsSlidingInOrOut = fromState == NORMAL && toState.overviewUi
- || fromState.overviewUi && toState == NORMAL;
// Scroll to the workspace card before changing to the NORMAL state.
+ LauncherState fromState = mLauncher.getStateManager().getState();
int currPage = mRecentsView.getCurrentPage();
if (fromState.overviewUi && toState == NORMAL && currPage != 0 && !config.userControlled) {
int maxSnapDuration = PagedView.SLOW_PAGE_SNAP_ANIMATION_DURATION;
@@ -83,19 +80,27 @@
mTransitionProgress.animateToValue(toState.overviewUi ? 1 : 0);
progressAnim.setDuration(config.duration);
progressAnim.setInterpolator(Interpolators.LINEAR);
- progressAnim.addListener(new AnimationSuccessListener() {
-
- @Override
- public void onAnimationSuccess(Animator animator) {
- mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
- }
- });
builder.play(progressAnim);
ObjectAnimator visibilityAnim = animateVisibility(toState.overviewUi);
visibilityAnim.setDuration(config.duration);
visibilityAnim.setInterpolator(Interpolators.LINEAR);
builder.play(visibilityAnim);
+
+ int direction = mRecentsView.isRtl() ? -1 : 1;
+ float fromTranslationX = fromState.getOverviewTranslationX(mLauncher) * direction;
+ float toTranslationX = toState.getOverviewTranslationX(mLauncher) * direction;
+ ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(mRecentsView, View.TRANSLATION_X,
+ fromTranslationX, toTranslationX);
+ translationXAnim.setDuration(config.duration);
+ translationXAnim.setInterpolator(Interpolators.ACCEL);
+ if (toState.overviewUi) {
+ translationXAnim.addUpdateListener(valueAnimator -> {
+ // While animating into recents, update the visible task data as needed
+ mRecentsView.loadVisibleTaskData();
+ });
+ }
+ builder.play(translationXAnim);
}
public void setVisibility(boolean isVisible) {
@@ -131,15 +136,6 @@
private void onTransitionProgress() {
applyProgress();
- if (mIsRecentsSlidingInOrOut) {
- float interpolatedProgress = ACCEL.getInterpolation(mTransitionProgress.value);
- // Slide in from the side as we swipe.
- int translation = mRecentsView.getWidth();
- if (mRecentsView.isRtl()) {
- translation = -translation;
- }
- mRecentsView.setTranslationX(translation * (1 - interpolatedProgress));
- }
}
private void onVisibilityProgress() {
@@ -148,9 +144,5 @@
private void applyProgress() {
mRecentsView.setAlpha(mTransitionProgress.value * mVisibilityMultiplier.value);
- if (mIsRecentsSlidingInOrOut) {
- // While animating into recents, update the visible task data as needed
- mRecentsView.loadVisibleTaskData();
- }
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 9051cfb..789185a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -36,11 +36,13 @@
public static TouchController[] createTouchControllers(Launcher launcher) {
if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) {
return new TouchController[] {
+ launcher.getDragController(),
new EdgeSwipeController(launcher),
new TwoStepSwipeController(launcher),
new OverviewSwipeController(launcher)};
} else {
return new TouchController[] {
+ launcher.getDragController(),
new TwoStepSwipeController(launcher),
new OverviewSwipeController(launcher)};
}
diff --git a/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java b/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java
new file mode 100644
index 0000000..f5e1f6e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.os.Build;
+import android.provider.SearchIndexablesContract.XmlResource;
+import android.provider.SearchIndexablesProvider;
+import android.util.Xml;
+
+import com.android.launcher3.R;
+import com.android.launcher3.graphics.IconShapeOverride;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
+import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
+import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS;
+
+@TargetApi(Build.VERSION_CODES.O)
+public class LauncherSearchIndexablesProvider extends SearchIndexablesProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor queryXmlResources(String[] strings) {
+ MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
+ ResolveInfo settingsActivity = getContext().getPackageManager().resolveActivity(
+ new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
+ .setPackage(getContext().getPackageName()), 0);
+ cursor.newRow()
+ .add(XmlResource.COLUMN_XML_RESID, R.xml.indexable_launcher_prefs)
+ .add(XmlResource.COLUMN_INTENT_ACTION, Intent.ACTION_APPLICATION_PREFERENCES)
+ .add(XmlResource.COLUMN_INTENT_TARGET_PACKAGE, getContext().getPackageName())
+ .add(XmlResource.COLUMN_INTENT_TARGET_CLASS, settingsActivity.activityInfo.name);
+ return cursor;
+ }
+
+ @Override
+ public Cursor queryRawData(String[] projection) {
+ return new MatrixCursor(INDEXABLES_RAW_COLUMNS);
+ }
+
+ @Override
+ public Cursor queryNonIndexableKeys(String[] projection) {
+ MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS);
+ if (!getContext().getSystemService(LauncherApps.class).hasShortcutHostPermission()) {
+ // We are not the current launcher. Hide all preferences
+ try (XmlResourceParser parser = getContext().getResources()
+ .getXml(R.xml.indexable_launcher_prefs)) {
+ final int depth = parser.getDepth();
+ final int[] attrs = new int[] { android.R.attr.key };
+ int type;
+ while (((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG) {
+ TypedArray a = getContext().obtainStyledAttributes(
+ Xml.asAttributeSet(parser), attrs);
+ cursor.addRow(new String[] {a.getString(0)});
+ a.recycle();
+ }
+ }
+ } catch (IOException |XmlPullParserException e) {
+ throw new RuntimeException(e);
+ }
+ } else if (!IconShapeOverride.isSupported(getContext())) {
+ cursor.addRow(new String[] {IconShapeOverride.KEY_PREFERENCE});
+ }
+ return cursor;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 598c34d..1d443fd 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -15,17 +15,25 @@
*/
package com.android.quickstep;
+import android.app.ActivityOptions;
import android.os.Bundle;
+import android.view.View;
-import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
+import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.views.BaseDragLayer;
/**
* A simple activity to show the recently launched tasks
*/
-public class RecentsActivity extends BaseActivity {
+public class RecentsActivity extends BaseDraggingActivity {
+
+ private RecentsRootView mRecentsRootView;
+ private FallbackRecentsView mFallbackRecentsView;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -39,5 +47,30 @@
: new InvariantDeviceProfile(this).getDeviceProfile(this));
setContentView(R.layout.fallback_recents_activity);
+ mRecentsRootView = findViewById(R.id.drag_layer);
+ mFallbackRecentsView = findViewById(R.id.overview_panel);
}
+
+ @Override
+ public BaseDragLayer getDragLayer() {
+ return mRecentsRootView;
+ }
+
+ @Override
+ public <T extends View> T getOverviewPanel() {
+ return (T) mFallbackRecentsView;
+ }
+
+ @Override
+ public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
+ return null;
+ }
+
+ @Override
+ public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
+ return null;
+ }
+
+ @Override
+ public void invalidateParent(ItemInfo info) { }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsRootView.java b/quickstep/src/com/android/quickstep/RecentsRootView.java
index 3c69dbf..24785f9 100644
--- a/quickstep/src/com/android/quickstep/RecentsRootView.java
+++ b/quickstep/src/com/android/quickstep/RecentsRootView.java
@@ -21,17 +21,19 @@
import android.util.AttributeSet;
import com.android.launcher3.BaseActivity;
-import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
-public class RecentsRootView extends InsettableFrameLayout {
+public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
private final BaseActivity mActivity;
public RecentsRootView(Context context, AttributeSet attrs) {
super(context, attrs);
mActivity = BaseActivity.fromContext(context);
+ mControllers = new TouchController[0];
}
@TargetApi(23)
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index eebfb91..08be0c8 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -30,13 +32,16 @@
import android.view.ViewTreeObserver.OnPreDrawListener;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.InstantAppResolver;
import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
@@ -57,6 +62,7 @@
public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
private static final String TAG = "TaskSystemShortcut";
+ private static final int DISMISS_TASK_DURATION = 300;
protected T mSystemShortcut;
@@ -70,11 +76,12 @@
}
@Override
- public View.OnClickListener getOnClickListener(Launcher launcher, ItemInfo itemInfo) {
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
return null;
}
- public View.OnClickListener getOnClickListener(final Launcher launcher, final TaskView view) {
+ public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) {
Task task = view.getTask();
ShortcutInfo dummyInfo = new ShortcutInfo();
@@ -82,14 +89,14 @@
ComponentName component = task.getTopComponent();
dummyInfo.intent.setComponent(component);
dummyInfo.user = UserHandle.of(task.key.userId);
- dummyInfo.title = TaskUtils.getTitle(launcher, task);
+ dummyInfo.title = TaskUtils.getTitle(activity, task);
- return getOnClickListenerForTask(launcher, task, dummyInfo);
+ return getOnClickListenerForTask(activity, task, dummyInfo);
}
- protected View.OnClickListener getOnClickListenerForTask(final Launcher launcher,
- final Task task, final ItemInfo dummyInfo) {
- return mSystemShortcut.getOnClickListener(launcher, dummyInfo);
+ protected View.OnClickListener getOnClickListenerForTask(
+ BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) {
+ return mSystemShortcut.getOnClickListener(activity, dummyInfo);
}
public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
@@ -98,10 +105,13 @@
}
}
- public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener {
+ public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener,
+ DeviceProfile.OnDeviceProfileChangeListener, View.OnLayoutChangeListener {
private Handler mHandler;
+ private RecentsView mRecentsView;
private TaskView mTaskView;
+ private BaseDraggingActivity mActivity;
public SplitScreen() {
super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
@@ -109,22 +119,27 @@
}
@Override
- public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) {
- if (launcher.getDeviceProfile().isMultiWindowMode) {
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, TaskView taskView) {
+ if (activity.getDeviceProfile().isMultiWindowMode) {
return null;
}
final Task task = taskView.getTask();
+ final int taskId = task.key.id;
if (!task.isDockable) {
return null;
}
+ mActivity = activity;
+ mRecentsView = activity.getOverviewPanel();
mTaskView = taskView;
+ final TaskThumbnailView thumbnailView = taskView.getThumbnail();
return (v -> {
- AbstractFloatingView.closeOpenViews(launcher, true,
+ AbstractFloatingView.closeOpenViews(activity, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
- if (ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key.id,
+ if (ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
ActivityOptionsCompat.makeSplitScreenOptions(true))) {
- ISystemUiProxy sysUiProxy = RecentsModel.getInstance(launcher).getSystemUiProxy();
+ ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
try {
sysUiProxy.onSplitScreenInvoked();
} catch (RemoteException e) {
@@ -132,26 +147,35 @@
return;
}
+ // Add a device profile change listener to kick off animating the side tasks
+ // once we enter multiwindow mode and relayout
+ activity.addOnDeviceProfileChangeListener(this);
+
final Runnable animStartedListener = () -> {
+ // Hide the task view and wait for the window to be resized
+ // TODO: Consider animating in launcher and do an in-place start activity
+ // afterwards
+ mRecentsView.addIgnoreResetTask(mTaskView);
+ mTaskView.setAlpha(0f);
mTaskView.getViewTreeObserver().addOnPreDrawListener(SplitScreen.this);
- launcher.<RecentsView>getOverviewPanel().removeView(taskView);
};
final int[] position = new int[2];
- taskView.getLocationOnScreen(position);
- final int width = (int) (taskView.getWidth() * taskView.getScaleX());
- final int height = (int) (taskView.getHeight() * taskView.getScaleY());
+ thumbnailView.getLocationOnScreen(position);
+ final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX());
+ final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY());
final Rect taskBounds = new Rect(position[0], position[1],
position[0] + width, position[1] + height);
Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
- taskBounds.width(), taskBounds.height(), taskView, 1f, Color.BLACK);
+ taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
+ Color.BLACK);
AppTransitionAnimationSpecsFuture future =
new AppTransitionAnimationSpecsFuture(mHandler) {
@Override
public List<AppTransitionAnimationSpecCompat> composeSpecs() {
return Collections.singletonList(new AppTransitionAnimationSpecCompat(
- task.key.id, thumbnail, taskBounds));
+ taskId, thumbnail, taskBounds));
}
};
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
@@ -166,6 +190,31 @@
WindowManagerWrapper.getInstance().endProlongedAnimations();
return true;
}
+
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ mActivity.removeOnDeviceProfileChangeListener(this);
+ if (dp.isMultiWindowMode) {
+ mTaskView.getRootView().addOnLayoutChangeListener(this);
+ }
+ }
+
+ @Override
+ public void onLayoutChange(View v, int l, int t, int r, int b,
+ int oldL, int oldT, int oldR, int oldB) {
+ mTaskView.getRootView().removeOnLayoutChangeListener(this);
+ mRecentsView.removeIgnoreResetTask(mTaskView);
+
+ // Start animating in the side pages once launcher has been resized
+ PendingAnimation pendingAnim = mRecentsView.createTaskDismissAnimation(mTaskView,
+ false, false, DISMISS_TASK_DURATION);
+ AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(
+ pendingAnim.anim, DISMISS_TASK_DURATION);
+ controller.dispatchOnStart();
+ controller.setEndAction(() -> pendingAnim.finish(true));
+ controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
+ controller.start();
+ }
}
public static class Pin extends TaskSystemShortcut {
@@ -178,8 +227,9 @@
}
@Override
- public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) {
- ISystemUiProxy sysUiProxy = RecentsModel.getInstance(launcher).getSystemUiProxy();
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, TaskView taskView) {
+ ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
if (sysUiProxy == null) {
return null;
}
@@ -211,11 +261,11 @@
}
@Override
- protected View.OnClickListener getOnClickListenerForTask(Launcher launcher, Task task,
- ItemInfo itemInfo) {
- if (InstantAppResolver.newInstance(launcher).isInstantApp(launcher,
+ protected View.OnClickListener getOnClickListenerForTask(
+ BaseDraggingActivity activity, Task task, ItemInfo itemInfo) {
+ if (InstantAppResolver.newInstance(activity).isInstantApp(activity,
task.getTopComponent().getPackageName())) {
- return mSystemShortcut.createOnClickListener(launcher, itemInfo);
+ return mSystemShortcut.createOnClickListener(activity, itemInfo);
}
return null;
}
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index b31d42f..2df951b 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -16,12 +16,12 @@
package com.android.quickstep;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.Log;
-import com.android.launcher3.Launcher;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.systemui.shared.recents.model.Task;
@@ -34,10 +34,10 @@
private static final String TAG = "TaskUtils";
- public static CharSequence getTitle(Launcher launcher, Task task) {
- LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(launcher);
- UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(launcher);
- PackageManager packageManager = launcher.getPackageManager();
+ public static CharSequence getTitle(Context context, Task task) {
+ LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(context);
+ UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(context);
+ PackageManager packageManager = context.getPackageManager();
UserHandle user = UserHandle.of(task.key.userId);
ApplicationInfo applicationInfo = launcherAppsCompat.getApplicationInfo(
task.getTopComponent().getPackageName(), 0, user);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 14476f7..f6eed58 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -30,6 +30,7 @@
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.graphics.Rect;
import android.os.Build;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.SparseBooleanArray;
import android.view.KeyEvent;
@@ -105,6 +106,9 @@
private PendingAnimation mPendingAnimation;
+ // Keeps track of task views whose visual state should not be reset
+ private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
+
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
@@ -262,7 +266,10 @@
public void resetTaskVisuals() {
for (int i = getChildCount() - 1; i >= 0; i--) {
- ((TaskView) getChildAt(i)).resetVisualProperties();
+ TaskView taskView = (TaskView) getChildAt(i);
+ if (!mIgnoreResetTaskViews.contains(taskView)) {
+ taskView.resetVisualProperties();
+ }
}
updateCurveProperties();
@@ -513,7 +520,16 @@
public float linearInterpolation;
}
- public PendingAnimation createTaskDismissAnimation(TaskView taskView, long duration) {
+ public void addIgnoreResetTask(TaskView taskView) {
+ mIgnoreResetTaskViews.add(taskView);
+ }
+
+ public void removeIgnoreResetTask(TaskView taskView) {
+ mIgnoreResetTaskViews.remove(taskView);
+ }
+
+ public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
+ boolean removeTask, long duration) {
if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
@@ -544,9 +560,11 @@
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child == taskView) {
- addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
- addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
- duration, LINEAR, anim);
+ if (animateTaskView) {
+ addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
+ addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
+ duration, LINEAR, anim);
+ }
} else {
int scrollDiff = newScroll[i] - oldScroll[i] + maxScrollDiff;
if (scrollDiff != 0) {
@@ -564,12 +582,16 @@
}
// Add a tiny bit of translation Z, so that it draws on top of other views
- taskView.setTranslationZ(0.1f);
+ if (animateTaskView) {
+ taskView.setTranslationZ(0.1f);
+ }
mPendingAnimation = pendingAnimation;
mPendingAnimation.addEndListener((isSuccess) -> {
if (isSuccess) {
- ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
+ if (removeTask) {
+ ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
+ }
removeView(taskView);
if (getChildCount() == 0) {
onAllTasksRemoved();
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 94f440d..30cbcdf 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -32,14 +32,14 @@
import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskUtils;
@@ -60,7 +60,7 @@
private static final long OPEN_CLOSE_DURATION = 220;
- private Launcher mLauncher;
+ private BaseDraggingActivity mActivity;
private TextView mTaskIconAndName;
private AnimatorSet mOpenCloseAnimator;
private TaskView mTaskView;
@@ -72,7 +72,7 @@
public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mLauncher = Launcher.getLauncher(context);
+ mActivity = BaseDraggingActivity.fromContext(context);
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@Override
@@ -92,7 +92,7 @@
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- DragLayer dl = mLauncher.getDragLayer();
+ BaseDragLayer dl = mActivity.getDragLayer();
if (!dl.isEventOverView(this, ev)) {
// TODO: log this once we have a new container type for it?
close(true);
@@ -122,9 +122,9 @@
}
public static boolean showForTask(TaskView taskView) {
- Launcher launcher = Launcher.getLauncher(taskView.getContext());
- final TaskMenuView taskMenuView = (TaskMenuView) launcher.getLayoutInflater().inflate(
- R.layout.task_menu, launcher.getDragLayer(), false);
+ BaseDraggingActivity activity = BaseDraggingActivity.fromContext(taskView.getContext());
+ final TaskMenuView taskMenuView = (TaskMenuView) activity.getLayoutInflater().inflate(
+ R.layout.task_menu, activity.getDragLayer(), false);
return taskMenuView.populateAndShowForTask(taskView);
}
@@ -132,7 +132,7 @@
if (isAttachedToWindow()) {
return false;
}
- mLauncher.getDragLayer().addView(this);
+ mActivity.getDragLayer().addView(this);
mTaskView = taskView;
addMenuOptions(mTaskView);
orientAroundTaskView(mTaskView);
@@ -145,11 +145,11 @@
int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
icon.setBounds(0, 0, iconSize, iconSize);
mTaskIconAndName.setCompoundDrawables(null, icon, null, null);
- mTaskIconAndName.setText(TaskUtils.getTitle(mLauncher, taskView.getTask()));
+ mTaskIconAndName.setText(TaskUtils.getTitle(getContext(), taskView.getTask()));
mTaskIconAndName.setOnClickListener(v -> close(true));
for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
- OnClickListener onClickListener = menuOption.getOnClickListener(mLauncher, taskView);
+ OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, taskView);
if (onClickListener != null) {
addMenuOption(menuOption, onClickListener);
}
@@ -157,7 +157,7 @@
}
private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) {
- DeepShortcutView menuOptionView = (DeepShortcutView) mLauncher.getLayoutInflater().inflate(
+ DeepShortcutView menuOptionView = (DeepShortcutView) mActivity.getLayoutInflater().inflate(
R.layout.system_shortcut, this, false);
menuOptionView.getIconView().setBackgroundResource(menuOption.iconResId);
menuOptionView.getBubbleText().setText(menuOption.labelResId);
@@ -167,8 +167,8 @@
private void orientAroundTaskView(TaskView taskView) {
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- mLauncher.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
- Rect insets = mLauncher.getDragLayer().getInsets();
+ mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
+ Rect insets = mActivity.getDragLayer().getInsets();
int x = sTempRect.left + (sTempRect.width() - getMeasuredWidth()) / 2 - insets.left;
setX(Utilities.isRtl(getResources()) ? -x : x);
setY(sTempRect.top - mTaskIconAndName.getPaddingTop() - insets.top);
@@ -211,7 +211,7 @@
private void closeComplete() {
mIsOpen = false;
- mLauncher.getDragLayer().removeView(this);
+ mActivity.getDragLayer().removeView(this);
}
private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 7a575ad..c20b577 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -28,6 +28,7 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.quickstep.views.RecentsView.PageCallbacks;
@@ -110,7 +111,8 @@
if (mTask != null) {
final ActivityOptions opts;
if (animate) {
- opts = Launcher.getLauncher(getContext()).getActivityLaunchOptions(this, false);
+ opts = BaseDraggingActivity.fromContext(getContext())
+ .getActivityLaunchOptions(this, false);
} else {
opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0);
}
diff --git a/res/layout/launcher_preference.xml b/res/layout/launcher_preference.xml
new file mode 100644
index 0000000..ed0ea7c
--- /dev/null
+++ b/res/layout/launcher_preference.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher3.views.HighlightableListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:cacheColorHint="@android:color/transparent"
+ android:clipToPadding="false"
+ android:drawSelectorOnTop="false"
+ android:orientation="vertical"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ android:scrollbarStyle="outsideOverlay" />
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index 3f727cf..a40afe1 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -119,6 +119,10 @@
<!-- Tag id used for view scrim -->
<item type="id" name="view_scrim" />
+ <!-- View IDs to store item highlight information -->
+ <item type="id" name="view_unhighlight_background" />
+ <item type="id" name="view_highlighted" />
+
<!-- Popup items -->
<integer name="config_popupOpenCloseDuration">150</integer>
<integer name="config_popupArrowOpenDuration">80</integer>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b39cb04..d971a36 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -196,6 +196,8 @@
<!-- Developer setting to change the shape of icons on home screen. [CHAR LIMIT=50] -->
<string name="icon_shape_override_label">Change icon shape</string>
+ <!-- Subtext explaining that the icons will only be affected on the home screen. This text follows the actual icon action: Change icon shape, on Home screen [CHAR LIMIT=100] -->
+ <string name="icon_shape_override_label_location">on Home screen</string>
<!-- Option to not change the icon shape on home screen and use the system default setting instead. [CHAR LIMIT=50] -->
<string name="icon_shape_system_default">Use system default</string>
<!-- Option to change the shape of the home screen icons to a square. [CHAR LIMIT=50] -->
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index fc5ce8f..f34cf0d 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -24,9 +24,9 @@
import android.view.View;
import android.widget.LinearLayout;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -92,7 +92,7 @@
public final void close(boolean animate) {
animate &= !Utilities.isPowerSaverOn(getContext());
handleClose(animate);
- Launcher.getLauncher(getContext()).getUserEventDispatcher()
+ BaseActivity.fromContext(getContext()).getUserEventDispatcher()
.resetElapsedContainerMillis("container closed");
}
@@ -120,8 +120,8 @@
}
protected static <T extends AbstractFloatingView> T getOpenView(
- Launcher launcher, @FloatingViewType int type) {
- DragLayer dragLayer = launcher.getDragLayer();
+ BaseDraggingActivity activity, @FloatingViewType int type) {
+ BaseDragLayer dragLayer = activity.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--) {
@@ -136,16 +136,17 @@
return null;
}
- public static void closeOpenContainer(Launcher launcher, @FloatingViewType int type) {
- AbstractFloatingView view = getOpenView(launcher, type);
+ public static void closeOpenContainer(BaseDraggingActivity activity,
+ @FloatingViewType int type) {
+ AbstractFloatingView view = getOpenView(activity, type);
if (view != null) {
view.close(true);
}
}
- public static void closeOpenViews(Launcher launcher, boolean animate,
+ public static void closeOpenViews(BaseDraggingActivity activity, boolean animate,
@FloatingViewType int type) {
- DragLayer dragLayer = launcher.getDragLayer();
+ BaseDragLayer dragLayer = activity.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--) {
@@ -159,16 +160,16 @@
}
}
- public static void closeAllOpenViews(Launcher launcher, boolean animate) {
- closeOpenViews(launcher, animate, TYPE_ALL);
- launcher.finishAutoCancelActionMode();
+ public static void closeAllOpenViews(BaseDraggingActivity activity, boolean animate) {
+ closeOpenViews(activity, animate, TYPE_ALL);
+ activity.finishAutoCancelActionMode();
}
- public static void closeAllOpenViews(Launcher launcher) {
- closeAllOpenViews(launcher, true);
+ public static void closeAllOpenViews(BaseDraggingActivity activity) {
+ closeAllOpenViews(activity, true);
}
- public static AbstractFloatingView getTopOpenView(Launcher launcher) {
- return getOpenView(launcher, TYPE_ALL);
+ public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) {
+ return getOpenView(activity, TYPE_ALL);
}
}
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 77a430b..02d70c4 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -98,9 +98,12 @@
mDPChangeListeners.add(listener);
}
+ public void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
+ mDPChangeListeners.remove(listener);
+ }
+
protected void dispatchDeviceProfileChanged() {
- int count = mDPChangeListeners.size();
- for (int i = 0; i < count; i++) {
+ for (int i = mDPChangeListeners.size() - 1; i >= 0; i--) {
mDPChangeListeners.get(i).onDeviceProfileChanged(mDeviceProfile);
}
}
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
new file mode 100644
index 0000000..35edaf4
--- /dev/null
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.StrictMode;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.View;
+import android.widget.Toast;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.views.BaseDragLayer;
+
+/**
+ * Extension of BaseActivity allowing support for drag-n-drop
+ */
+public abstract class BaseDraggingActivity extends BaseActivity {
+
+ private static final String TAG = "BaseDraggingActivity";
+
+ // The Intent extra that defines whether to ignore the launch animation
+ private static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
+ "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
+
+ // When starting an action mode, setting this tag will cause the action mode to be cancelled
+ // automatically when user interacts with the launcher.
+ public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
+
+ private ActionMode mCurrentActionMode;
+ protected boolean mIsSafeModeEnabled;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mIsSafeModeEnabled = getPackageManager().isSafeMode();
+ }
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {
+ super.onActionModeStarted(mode);
+ mCurrentActionMode = mode;
+ }
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {
+ super.onActionModeFinished(mode);
+ mCurrentActionMode = null;
+ }
+
+ public boolean finishAutoCancelActionMode() {
+ if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
+ mCurrentActionMode.finish();
+ return true;
+ }
+ return false;
+ }
+
+ public abstract BaseDragLayer getDragLayer();
+
+ public abstract <T extends View> T getOverviewPanel();
+
+ public abstract BadgeInfo getBadgeInfoForItem(ItemInfo info);
+
+ public abstract void invalidateParent(ItemInfo info);
+
+ public static BaseDraggingActivity fromContext(Context context) {
+ if (context instanceof BaseDraggingActivity) {
+ return (BaseDraggingActivity) context;
+ }
+ return ((BaseDraggingActivity) ((ContextWrapper) context).getBaseContext());
+ }
+
+ public Rect getViewBounds(View v) {
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
+ }
+
+ public final Bundle getActivityLaunchOptionsAsBundle(View v, boolean useDefaultLaunchOptions) {
+ ActivityOptions activityOptions = getActivityLaunchOptions(v, useDefaultLaunchOptions);
+ return activityOptions == null ? null : activityOptions.toBundle();
+ }
+
+ public abstract ActivityOptions getActivityLaunchOptions(
+ View v, boolean useDefaultLaunchOptions);
+
+ public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+ if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
+ Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ // Only launch using the new animation if the shortcut has not opted out (this is a
+ // private contract between launcher and may be ignored in the future).
+ boolean useLaunchAnimation = (v != null) &&
+ !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
+ Bundle optsBundle = useLaunchAnimation
+ ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat())
+ : null;
+
+ UserHandle user = item == null ? null : item.user;
+
+ // Prepare intent
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (v != null) {
+ intent.setSourceBounds(getViewBounds(v));
+ }
+ try {
+ boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
+ && (item instanceof ShortcutInfo)
+ && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
+ || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+ && !((ShortcutInfo) item).isPromise();
+ if (isShortcut) {
+ // Shortcuts need some special checks due to legacy reasons.
+ startShortcutIntentSafely(intent, optsBundle, item);
+ } else if (user == null || user.equals(Process.myUserHandle())) {
+ // Could be launching some bookkeeping activity
+ startActivity(intent, optsBundle);
+ } else {
+ LauncherAppsCompat.getInstance(this).startActivityForProfile(
+ intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
+ }
+ getUserEventDispatcher().logAppLaunch(v, intent);
+ return true;
+ } catch (ActivityNotFoundException|SecurityException e) {
+ Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
+ }
+ return false;
+ }
+
+ private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
+ try {
+ StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
+ try {
+ // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
+ // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
+ // is enabled by default on NYC.
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
+ .penaltyLog().build());
+
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ String id = ((ShortcutInfo) info).getDeepShortcutId();
+ String packageName = intent.getPackage();
+ DeepShortcutManager.getInstance(this).startShortcut(
+ packageName, id, intent.getSourceBounds(), optsBundle, info.user);
+ } else {
+ // Could be launching some bookkeeping activity
+ startActivity(intent, optsBundle);
+ }
+ } finally {
+ StrictMode.setVmPolicy(oldPolicy);
+ }
+ } catch (SecurityException e) {
+ if (!onErrorStartingShortcut(intent, info)) {
+ throw e;
+ }
+ }
+ }
+
+ protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
+ return false;
+ }
+}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 8b6d9f8..fc61155 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -44,7 +44,6 @@
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.badge.BadgeRenderer;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.PreloadIconDrawable;
@@ -65,7 +64,7 @@
private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
- private final Launcher mLauncher;
+ private final BaseDraggingActivity mActivity;
private Drawable mIcon;
private final boolean mCenterVertically;
@@ -133,8 +132,8 @@
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mLauncher = Launcher.getLauncher(context);
- DeviceProfile grid = mLauncher.getDeviceProfile();
+ mActivity = BaseDraggingActivity.fromContext(context);
+ DeviceProfile grid = mActivity.getDeviceProfile();
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
TypedArray a = context.obtainStyledAttributes(attrs,
@@ -164,7 +163,7 @@
mLongPressHelper = new CheckLongPressHelper(this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
- setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
+ setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
}
@@ -493,10 +492,10 @@
public void applyBadgeState(ItemInfo itemInfo, boolean animate) {
if (mIcon instanceof FastBitmapDrawable) {
boolean wasBadged = mBadgeInfo != null;
- mBadgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
+ mBadgeInfo = mActivity.getBadgeInfoForItem(itemInfo);
boolean isBadged = mBadgeInfo != null;
float newBadgeScale = isBadged ? 1f : 0;
- mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
+ mBadgeRenderer = mActivity.getDeviceProfile().mBadgeRenderer;
if (wasBadged || isBadged) {
// Animate when a badge is first added or when it is removed.
if (animate && (wasBadged ^ isBadged) && isShown()) {
@@ -572,15 +571,7 @@
applyFromApplicationInfo((AppInfo) info);
} else if (info instanceof ShortcutInfo) {
applyFromShortcutInfo((ShortcutInfo) info);
- FolderIconPreviewVerifier verifier =
- new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
- if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) {
- View folderIcon =
- mLauncher.getWorkspace().getHomescreenIconByItemId(info.container);
- if (folderIcon != null) {
- folderIcon.invalidate();
- }
- }
+ mActivity.invalidateParent(info);
} else if (info instanceof PackageItemInfo) {
applyFromPackageItemInfo((PackageItemInfo) info);
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0bbb90c..a38ce07 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -25,7 +25,6 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import android.Manifest;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -47,7 +46,6 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@@ -60,7 +58,6 @@
import android.text.method.TextKeyListener;
import android.util.Log;
import android.util.SparseArray;
-import android.view.ActionMode;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
@@ -73,14 +70,13 @@
import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
@@ -88,6 +84,7 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dynamicui.WallpaperColorInfo;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.FileLog;
@@ -139,7 +136,7 @@
/**
* Default launcher application.
*/
-public class Launcher extends BaseActivity implements LauncherExterns, LauncherModel.Callbacks,
+public class Launcher extends BaseDraggingActivity implements LauncherExterns, LauncherModel.Callbacks,
LauncherProviderChangeListener, WallpaperColorInfo.OnThemeChangeListener {
public static final String TAG = "Launcher";
static final boolean LOGD = false;
@@ -166,10 +163,6 @@
*/
protected static final int REQUEST_LAST = 100;
- // The Intent extra that defines whether to ignore the launch animation
- static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
- "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
-
// Type: int
private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
// Type: int
@@ -181,14 +174,8 @@
// Type: SparseArray<Parcelable>
private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
- // When starting an action mode, setting this tag will cause the action mode to be cancelled
- // automatically when user interacts with the launcher.
- public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
-
private LauncherStateManager mStateManager;
- private boolean mIsSafeModeEnabled;
-
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
// How long to wait before the new-shortcut animation automatically pans the workspace
@@ -255,7 +242,6 @@
private boolean mAppLaunchSuccess;
private RotationHelper mRotationHelper;
- private ActionMode mCurrentActionMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -288,7 +274,6 @@
initDeviceProfile(app.getInvariantDeviceProfile());
mSharedPrefs = Utilities.getPrefs(this);
- mIsSafeModeEnabled = getPackageManager().isSafeMode();
mIconCache = app.getIconCache();
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
@@ -482,6 +467,22 @@
return mPopupDataProvider;
}
+ @Override
+ public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
+ return mPopupDataProvider.getBadgeInfoForItem(info);
+ }
+
+ @Override
+ public void invalidateParent(ItemInfo info) {
+ FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(getDeviceProfile().inv);
+ if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) {
+ View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container);
+ if (folderIcon != null) {
+ folderIcon.invalidate();
+ }
+ }
+ }
+
/**
* Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
* a configuration step, this allows the proper animations to run after other transitions.
@@ -941,7 +942,7 @@
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
// Setup the drag layer
- mDragLayer.setup(this, mDragController);
+ mDragLayer.setup(mDragController);
mWorkspace.setup(mDragController);
// Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
@@ -1180,6 +1181,7 @@
return (LauncherRootView) mLauncherView;
}
+ @Override
public DragLayer getDragLayer() {
return mDragLayer;
}
@@ -1672,119 +1674,45 @@
}
}
- private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
- try {
- StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
- try {
- // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
- // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
- // is enabled by default on NYC.
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
- .penaltyLog().build());
-
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- String id = ((ShortcutInfo) info).getDeepShortcutId();
- String packageName = intent.getPackage();
- DeepShortcutManager.getInstance(this).startShortcut(
- packageName, id, intent.getSourceBounds(), optsBundle, info.user);
- } else {
- // Could be launching some bookkeeping activity
- startActivity(intent, optsBundle);
- }
- } finally {
- StrictMode.setVmPolicy(oldPolicy);
- }
- } catch (SecurityException e) {
- // Due to legacy reasons, direct call shortcuts require Launchers to have the
- // corresponding permission. Show the appropriate permission prompt if that
- // is the case.
- if (intent.getComponent() == null
- && Intent.ACTION_CALL.equals(intent.getAction())
- && checkSelfPermission(Manifest.permission.CALL_PHONE) !=
- PackageManager.PERMISSION_GRANTED) {
-
- setWaitingForResult(PendingRequestArgs
- .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info));
- requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
- REQUEST_PERMISSION_CALL_PHONE);
- } else {
- // No idea why this was thrown.
- throw e;
- }
- }
- }
-
- public Bundle getActivityLaunchOptionsAsBundle(View v, boolean useDefaultLaunchOptions) {
- ActivityOptions activityOptions = getActivityLaunchOptions(v, useDefaultLaunchOptions);
- return activityOptions == null ? null : activityOptions.toBundle();
- }
-
@TargetApi(Build.VERSION_CODES.M)
+ @Override
public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
return useDefaultLaunchOptions
? mAppTransitionManager.getDefaultActivityLaunchOptions(this, v)
: mAppTransitionManager.getActivityLaunchOptions(this, v);
}
- public Rect getViewBounds(View v) {
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
+ @TargetApi(Build.VERSION_CODES.M)
+ @Override
+ protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
+ // Due to legacy reasons, direct call shortcuts require Launchers to have the
+ // corresponding permission. Show the appropriate permission prompt if that
+ // is the case.
+ if (intent.getComponent() == null
+ && Intent.ACTION_CALL.equals(intent.getAction())
+ && checkSelfPermission(android.Manifest.permission.CALL_PHONE) !=
+ PackageManager.PERMISSION_GRANTED) {
+
+ setWaitingForResult(PendingRequestArgs
+ .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info));
+ requestPermissions(new String[]{android.Manifest.permission.CALL_PHONE},
+ REQUEST_PERMISSION_CALL_PHONE);
+ return true;
+ } else {
+ return false;
+ }
}
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
- mAppLaunchSuccess = false;
- if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
- Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
- return mAppLaunchSuccess;
- }
-
- // Only launch using the new animation if the shortcut has not opted out (this is a
- // private contract between launcher and may be ignored in the future).
- boolean useLaunchAnimation = (v != null) &&
- !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
- Bundle optsBundle = useLaunchAnimation
- ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat())
- : null;
-
- UserHandle user = item == null ? null : item.user;
-
- // Prepare intent
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (v != null) {
- intent.setSourceBounds(getViewBounds(v));
- }
- try {
- boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
- && (item instanceof ShortcutInfo)
- && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
- || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
- && !((ShortcutInfo) item).isPromise();
- if (isShortcut) {
- // Shortcuts need some special checks due to legacy reasons.
- startShortcutIntentSafely(intent, optsBundle, item);
- } else if (user == null || user.equals(Process.myUserHandle())) {
- // Could be launching some bookkeeping activity
- startActivity(intent, optsBundle);
- } else {
- LauncherAppsCompat.getInstance(this).startActivityForProfile(
- intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
- }
-
- if (v instanceof BubbleTextView) {
- // This is set to the view that launched the activity that navigated the user away
- // from launcher. Since there is no callback for when the activity has finished
- // launching, enable the press state and keep this reference to reset the press
- // state when we return to launcher.
- BubbleTextView btv = (BubbleTextView) v;
- btv.setStayPressed(true);
- setOnResumeCallback(btv);
- }
- mAppLaunchSuccess = true;
- getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115
- } catch (ActivityNotFoundException|SecurityException e) {
- Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
+ mAppLaunchSuccess = super.startActivitySafely(v, intent, item);
+ if (mAppLaunchSuccess && v instanceof BubbleTextView) {
+ // This is set to the view that launched the activity that navigated the user away
+ // from launcher. Since there is no callback for when the activity has finished
+ // launching, enable the press state and keep this reference to reset the press
+ // state when we return to launcher.
+ BubbleTextView btv = (BubbleTextView) v;
+ btv.setStayPressed(true);
+ setOnResumeCallback(btv);
}
return mAppLaunchSuccess;
}
@@ -2515,26 +2443,6 @@
return ((Launcher) ((ContextWrapper) context).getBaseContext());
}
- @Override
- public void onActionModeStarted(ActionMode mode) {
- super.onActionModeStarted(mode);
- mCurrentActionMode = mode;
- }
-
- @Override
- public void onActionModeFinished(ActionMode mode) {
- super.onActionModeFinished(mode);
- mCurrentActionMode = null;
- }
-
- public boolean finishAutoCancelActionMode() {
- if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
- mCurrentActionMode.finish();
- return true;
- }
- return false;
- }
-
/**
* Callback for listening for onResume
*/
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 4e6bcdc..e5d8f47 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -165,6 +165,10 @@
return 1f;
}
+ public float getOverviewTranslationX(Launcher launcher) {
+ return launcher.getDragLayer().getMeasuredWidth();
+ }
+
public void onStateEnabled(Launcher launcher) {
dispatchWindowStateChanged(launcher);
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 6e8cacd..9eff84b 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -305,9 +305,9 @@
}
private void updatePageIndicator() {
- // Update the page indicator (when we aren't reordering)
if (mPageIndicator != null) {
mPageIndicator.setPageDescription(getPageIndicatorDescription());
+ mPageIndicator.setActiveMarker(getNextPage());
}
}
protected void pageBeginTransition() {
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index 6a4e93b..7fa0e52 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -31,11 +31,17 @@
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.provider.Settings;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Adapter;
import com.android.launcher3.graphics.IconShapeOverride;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.SettingsObserver;
import com.android.launcher3.views.ButtonPreference;
+import com.android.launcher3.views.HighlightableListView;
/**
* Settings activity for Launcher. Currently implements the following setting: Allow rotation
@@ -48,6 +54,10 @@
/** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
+ private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+ private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
+ private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -55,11 +65,15 @@
if (savedInstanceState == null) {
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
- .replace(android.R.id.content, new LauncherSettingsFragment())
+ .replace(android.R.id.content, getNewFragment())
.commit();
}
}
+ protected PreferenceFragment getNewFragment() {
+ return new LauncherSettingsFragment();
+ }
+
/**
* This fragment shows the launcher preferences.
*/
@@ -67,9 +81,22 @@
private IconBadgingObserver mIconBadgingObserver;
+ private String mPreferenceKey;
+ private boolean mPreferenceHighlighted = false;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.launcher_preference, container, false);
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
+ }
+
getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
addPreferencesFromResource(R.xml.launcher_preferences);
@@ -101,6 +128,43 @@
}
@Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ Intent intent = getActivity().getIntent();
+ mPreferenceKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
+ if (isAdded() && !mPreferenceHighlighted && !TextUtils.isEmpty(mPreferenceKey)) {
+ getView().postDelayed(this::highlightPreference, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ }
+ }
+
+ private void highlightPreference() {
+ HighlightableListView list = getView().findViewById(android.R.id.list);
+ Preference pref = findPreference(mPreferenceKey);
+ Adapter adapter = list.getAdapter();
+ if (adapter == null) {
+ return;
+ }
+
+ // Find the position
+ int position = -1;
+ for (int i = adapter.getCount() - 1; i >= 0; i--) {
+ if (pref == adapter.getItem(i)) {
+ position = i;
+ break;
+ }
+ }
+ list.highlightPosition(position);
+ mPreferenceHighlighted = true;
+ }
+
+ @Override
public void onDestroy() {
if (mIconBadgingObserver != null) {
mIconBadgingObserver.unregister();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 9960953..68ad253 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3254,8 +3254,7 @@
&& v instanceof FolderIcon) {
FolderBadgeInfo folderBadgeInfo = new FolderBadgeInfo();
for (ShortcutInfo si : ((FolderInfo) info).contents) {
- folderBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider()
- .getBadgeInfoForItem(si));
+ folderBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(si));
}
((FolderIcon) v).setBadgeInfo(folderBadgeInfo);
}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index f5d0b24..8519365 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -31,21 +31,17 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
-import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
@@ -53,24 +49,20 @@
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
/**
* A ViewGroup that coordinates dragging across its descendants
*/
-public class DragLayer extends InsettableFrameLayout {
+public class DragLayer extends BaseDragLayer<Launcher> {
public static final int ANIMATION_END_DISAPPEAR = 0;
public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
- private final int[] mTmpXY = new int[2];
-
@Thunk DragController mDragController;
- private Launcher mLauncher;
-
// Variables relating to animation of views after drop
private ValueAnimator mDropAnim = null;
private final TimeInterpolator mCubicEaseOutInterpolator = Interpolators.DEACCEL_1_5;
@@ -79,9 +71,6 @@
@Thunk View mAnchorView = null;
private boolean mHoverPointClosesFolder = false;
- private final Rect mHitRect = new Rect();
-
- private TouchCompleteListener mTouchCompleteListener;
private int mTopViewIndex;
private int mChildCountOnLastUpdate = -1;
@@ -89,8 +78,6 @@
// Related to adjacent page hints
private final ViewGroupFocusHelper mFocusIndicatorHelper;
- protected TouchController[] mControllers;
- private TouchController mActiveController;
/**
* Used to create a new DragLayer from XML.
*
@@ -107,10 +94,9 @@
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
}
- public void setup(Launcher launcher, DragController dragController) {
- mLauncher = launcher;
+ public void setup(DragController dragController) {
mDragController = dragController;
- mControllers = UiFactory.createTouchControllers(mLauncher);
+ mControllers = UiFactory.createTouchControllers(mActivity);
}
public ViewGroupFocusHelper getFocusIndicatorHelper() {
@@ -123,7 +109,7 @@
}
public boolean isEventOverHotseat(MotionEvent ev) {
- return isEventOverView(mLauncher.getHotseat(), ev);
+ return isEventOverView(mActivity.getHotseat(), ev);
}
private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
@@ -131,12 +117,7 @@
}
private boolean isEventOverDropTargetBar(MotionEvent ev) {
- return isEventOverView(mLauncher.getDropTargetBar(), ev);
- }
-
- public boolean isEventOverView(View view, MotionEvent ev) {
- getDescendantRectRelativeToSelf(view, mHitRect);
- return mHitRect.contains((int) ev.getX(), (int) ev.getY());
+ return isEventOverView(mActivity.getDropTargetBar(), ev);
}
@Override
@@ -149,66 +130,33 @@
}
@Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
-
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- if (mTouchCompleteListener != null) {
- mTouchCompleteListener.onTouchComplete();
- }
- mTouchCompleteListener = null;
- } else if (action == MotionEvent.ACTION_DOWN) {
- mLauncher.finishAutoCancelActionMode();
- }
- return findActiveController(ev);
- }
-
- private boolean findActiveController(MotionEvent ev) {
- mActiveController = null;
-
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
- if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
- mActiveController = topView;
- return true;
- }
-
- if (mLauncher.getStateManager().getState().disableInteraction) {
+ protected boolean findActiveController(MotionEvent ev) {
+ if (mActivity.getStateManager().getState().disableInteraction) {
// You Shall Not Pass!!!
+ mActiveController = null;
return true;
}
-
- if (mDragController.onControllerInterceptTouchEvent(ev)) {
- mActiveController = mDragController;
- return true;
- }
-
- for (TouchController controller : mControllers) {
- if (controller.onControllerInterceptTouchEvent(ev)) {
- mActiveController = controller;
- return true;
- }
- }
- return false;
+ return super.findActiveController(ev);
}
@Override
public boolean onInterceptHoverEvent(MotionEvent ev) {
- if (mLauncher == null || mLauncher.getWorkspace() == null) {
+ if (mActivity == null || mActivity.getWorkspace() == null) {
return false;
}
- Folder currentFolder = Folder.getOpen(mLauncher);
+ Folder currentFolder = Folder.getOpen(mActivity);
if (currentFolder == null) {
return false;
} else {
- AccessibilityManager accessibilityManager = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ AccessibilityManager accessibilityManager = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
if (accessibilityManager.isTouchExplorationEnabled()) {
final int action = ev.getAction();
boolean isOverFolderOrSearchBar;
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
- (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+ (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
if (!isOverFolderOrSearchBar) {
sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
mHoverPointClosesFolder = true;
@@ -218,7 +166,7 @@
break;
case MotionEvent.ACTION_HOVER_MOVE:
isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
- (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+ (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
mHoverPointClosesFolder = true;
@@ -239,14 +187,22 @@
this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId));
}
+ @Override
+ public boolean onHoverEvent(MotionEvent ev) {
+ // If we've received this, we've already done the necessary handling
+ // in onInterceptHoverEvent. Return true to consume the event.
+ return false;
+ }
+
+
private boolean isInAccessibleDrag() {
- return mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
+ return mActivity.getAccessibilityDelegate().isInAccessibleDrag();
}
@Override
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
// Shortcuts can appear above folder
- View topView = AbstractFloatingView.getTopOpenView(mLauncher);
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null) {
if (child == topView) {
return super.onRequestSendAccessibilityEvent(child, event);
@@ -263,13 +219,13 @@
@Override
public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
- View topView = AbstractFloatingView.getTopOpenView(mLauncher);
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null) {
// Only add the top view as a child for accessibility when it is open
childrenForAccessibility.add(topView);
if (isInAccessibleDrag()) {
- childrenForAccessibility.add(mLauncher.getDropTargetBar());
+ childrenForAccessibility.add(mActivity.getDropTargetBar());
}
} else {
super.addChildrenForAccessibility(childrenForAccessibility);
@@ -277,103 +233,9 @@
}
@Override
- public boolean onHoverEvent(MotionEvent ev) {
- // If we've received this, we've already done the necessary handling
- // in onInterceptHoverEvent. Return true to consume the event.
- return false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- if (mTouchCompleteListener != null) {
- mTouchCompleteListener.onTouchComplete();
- }
- mTouchCompleteListener = null;
- }
-
- if (mActiveController != null) {
- return mActiveController.onControllerTouchEvent(ev);
- } else {
- // In case no child view handled the touch event, we may not get onIntercept anymore
- return findActiveController(ev);
- }
- }
-
- /**
- * Determine the rect of the descendant in this DragLayer's coordinates
- *
- * @param descendant The descendant whose coordinates we want to find.
- * @param r The rect into which to place the results.
- * @return The factor by which this descendant is scaled relative to this DragLayer.
- */
- public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
- mTmpXY[0] = 0;
- mTmpXY[1] = 0;
- float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
-
- r.set(mTmpXY[0], mTmpXY[1],
- (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()),
- (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight()));
- return scale;
- }
-
- public float getLocationInDragLayer(View child, int[] loc) {
- loc[0] = 0;
- loc[1] = 0;
- return getDescendantCoordRelativeToSelf(child, loc);
- }
-
- public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
- return getDescendantCoordRelativeToSelf(descendant, coord, false);
- }
-
- /**
- * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's
- * coordinates.
- *
- * @param descendant The descendant to which the passed coordinate is relative.
- * @param coord The coordinate that we want mapped.
- * @param includeRootScroll Whether or not to account for the scroll of the root descendant:
- * sometimes this is relevant as in a child's coordinates within the root descendant.
- * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
- * this scale factor is assumed to be equal in X and Y, and so if at any point this
- * assumption fails, we will need to return a pair of scale factors.
- */
- public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
- boolean includeRootScroll) {
- return Utilities.getDescendantCoordRelativeToAncestor(descendant, this,
- coord, includeRootScroll);
- }
-
- /**
- * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
- */
- public void mapCoordInSelfToDescendant(View descendant, int[] coord) {
- Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
- }
-
- public void getViewRectRelativeToSelf(View v, Rect r) {
- int[] loc = new int[2];
- getLocationInWindow(loc);
- int x = loc[0];
- int y = loc[1];
-
- v.getLocationInWindow(loc);
- int vX = loc[0];
- int vY = loc[1];
-
- int left = vX - x;
- int top = vY - y;
- r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
- }
-
- @Override
public boolean dispatchUnhandledMove(View focused, int direction) {
- // Consume the unhandled move if a container is open, to avoid switching pages underneath.
- boolean isContainerOpen = AbstractFloatingView.getTopOpenView(mLauncher) != null;
- return isContainerOpen || mDragController.dispatchUnhandledMove(focused, direction);
+ return super.dispatchUnhandledMove(focused, direction)
+ || mDragController.dispatchUnhandledMove(focused, direction);
}
@Override
@@ -386,91 +248,6 @@
}
}
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
- }
-
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- }
-
- // Override to allow type-checking of LayoutParams.
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams;
- }
-
- @Override
- protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return new LayoutParams(p);
- }
-
- public static class LayoutParams extends InsettableFrameLayout.LayoutParams {
- public int x, y;
- public boolean customPosition = false;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- }
-
- public LayoutParams(ViewGroup.LayoutParams lp) {
- super(lp);
- }
-
- public void setWidth(int width) {
- this.width = width;
- }
-
- public int getWidth() {
- return width;
- }
-
- public void setHeight(int height) {
- this.height = height;
- }
-
- public int getHeight() {
- return height;
- }
-
- public void setX(int x) {
- this.x = x;
- }
-
- public int getX() {
- return x;
- }
-
- public void setY(int y) {
- this.y = y;
- }
-
- public int getY() {
- return y;
- }
- }
-
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
- if (flp instanceof LayoutParams) {
- final LayoutParams lp = (LayoutParams) flp;
- if (lp.customPosition) {
- child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
- }
- }
- }
- }
-
public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
int duration) {
@@ -709,14 +486,14 @@
public void onViewAdded(View child) {
super.onViewAdded(child);
updateChildIndices();
- UiFactory.onLauncherStateOrFocusChanged(mLauncher);
+ UiFactory.onLauncherStateOrFocusChanged(mActivity);
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
updateChildIndices();
- UiFactory.onLauncherStateOrFocusChanged(mLauncher);
+ UiFactory.onLauncherStateOrFocusChanged(mActivity);
}
@Override
@@ -768,32 +545,4 @@
mFocusIndicatorHelper.draw(canvas);
super.dispatchDraw(canvas);
}
-
- @Override
- protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
- View topView = AbstractFloatingView.getTopOpenView(mLauncher);
- if (topView != null) {
- return topView.requestFocus(direction, previouslyFocusedRect);
- } else {
- return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
- }
- }
-
- @Override
- public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
- View topView = AbstractFloatingView.getTopOpenView(mLauncher);
- if (topView != null) {
- topView.addFocusables(views, direction);
- } else {
- super.addFocusables(views, direction, focusableMode);
- }
- }
-
- public void setTouchCompleteListener(TouchCompleteListener listener) {
- mTouchCompleteListener = listener;
- }
-
- public interface TouchCompleteListener {
- void onTouchComplete();
- }
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6c94273..cb5d872 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -568,7 +568,7 @@
@Override
public void onAdd(ShortcutInfo item, int rank) {
boolean wasBadged = mBadgeInfo.hasBadge();
- mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
+ mBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(item));
boolean isBadged = mBadgeInfo.hasBadge();
updateBadgeScale(wasBadged, isBadged);
invalidate();
@@ -578,7 +578,7 @@
@Override
public void onRemove(ShortcutInfo item) {
boolean wasBadged = mBadgeInfo.hasBadge();
- mBadgeInfo.subtractBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
+ mBadgeInfo.subtractBadgeInfo(mLauncher.getBadgeInfoForItem(item));
boolean isBadged = mBadgeInfo.hasBadge();
updateBadgeScale(wasBadged, isBadged);
invalidate();
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index e427a81..033fdf8 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -745,7 +745,7 @@
private void updateNotificationHeader() {
ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
- BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
+ BadgeInfo badgeInfo = mLauncher.getBadgeInfoForItem(itemInfo);
if (mNotificationItemView != null && badgeInfo != null) {
mNotificationItemView.updateHeader(
badgeInfo.getNotificationCount(), itemInfo.iconColor);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 2cc8dfa..a20149e 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -9,6 +9,7 @@
import android.view.View;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -27,7 +28,7 @@
*
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
*/
-public abstract class SystemShortcut extends ItemInfo {
+public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo {
public final int iconResId;
public final int labelResId;
@@ -36,10 +37,9 @@
this.labelResId = labelResId;
}
- public abstract View.OnClickListener getOnClickListener(final Launcher launcher,
- final ItemInfo itemInfo);
+ public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo);
- public static class Widgets extends SystemShortcut {
+ public static class Widgets extends SystemShortcut<Launcher> {
public Widgets() {
super(R.drawable.ic_widget, R.string.widget_button_text);
@@ -54,17 +54,14 @@
if (widgets == null) {
return null;
}
- return new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- AbstractFloatingView.closeAllOpenViews(launcher);
- WidgetsBottomSheet widgetsBottomSheet =
- (WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
- R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
- widgetsBottomSheet.populateAndShow(itemInfo);
- launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.WIDGETS_BUTTON, view);
- }
+ return (view) -> {
+ AbstractFloatingView.closeAllOpenViews(launcher);
+ WidgetsBottomSheet widgetsBottomSheet =
+ (WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
+ R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
+ widgetsBottomSheet.populateAndShow(itemInfo);
+ launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.WIDGETS_BUTTON, view);
};
}
}
@@ -75,18 +72,15 @@
}
@Override
- public View.OnClickListener getOnClickListener(final Launcher launcher,
- final ItemInfo itemInfo) {
- return new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Rect sourceBounds = launcher.getViewBounds(view);
- Bundle opts = launcher.getActivityLaunchOptionsAsBundle(view, false);
- new PackageManagerHelper(launcher).startDetailsActivityForInfo(
- itemInfo, sourceBounds, opts);
- launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.APPINFO_TARGET, view);
- }
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
+ return (view) -> {
+ Rect sourceBounds = activity.getViewBounds(view);
+ Bundle opts = activity.getActivityLaunchOptionsAsBundle(view, false);
+ new PackageManagerHelper(activity).startDetailsActivityForInfo(
+ itemInfo, sourceBounds, opts);
+ activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.APPINFO_TARGET, view);
};
}
}
@@ -97,28 +91,29 @@
}
@Override
- public View.OnClickListener getOnClickListener(final Launcher launcher,
- final ItemInfo itemInfo) {
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
boolean supportsWebUI = (itemInfo instanceof ShortcutInfo) &&
((ShortcutInfo) itemInfo).hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI);
boolean isInstantApp = false;
if (itemInfo instanceof com.android.launcher3.AppInfo) {
com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
- isInstantApp = InstantAppResolver.newInstance(launcher).isInstantApp(appInfo);
+ isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
}
boolean enabled = supportsWebUI || isInstantApp;
if (!enabled) {
return null;
}
- return createOnClickListener(launcher, itemInfo);
+ return createOnClickListener(activity, itemInfo);
}
- public View.OnClickListener createOnClickListener(Launcher launcher, ItemInfo itemInfo) {
+ public View.OnClickListener createOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
return view -> {
Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
itemInfo.getTargetComponent().getPackageName());
- launcher.startActivitySafely(view, intent, itemInfo);
- AbstractFloatingView.closeAllOpenViews(launcher);
+ activity.startActivitySafely(view, intent, itemInfo);
+ AbstractFloatingView.closeAllOpenViews(activity);
};
}
}
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
index 601a5ab..4485427 100644
--- a/src/com/android/launcher3/util/InstantAppResolver.java
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -22,7 +22,6 @@
import android.util.Log;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -47,8 +46,8 @@
return false;
}
- public boolean isInstantApp(Launcher launcher, String packageName) {
- PackageManager packageManager = launcher.getPackageManager();
+ public boolean isInstantApp(Context context, String packageName) {
+ PackageManager packageManager = context.getPackageManager();
try {
return isInstantApp(packageManager.getPackageInfo(packageName, 0).applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
new file mode 100644
index 0000000..489e59e
--- /dev/null
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.views;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.TouchController;
+
+import java.util.ArrayList;
+
+/**
+ * A viewgroup with utility methods for drag-n-drop and touch interception
+ */
+public abstract class BaseDragLayer<T extends BaseDraggingActivity> extends InsettableFrameLayout {
+
+ protected final int[] mTmpXY = new int[2];
+ protected final Rect mHitRect = new Rect();
+
+ protected final T mActivity;
+
+ protected TouchController[] mControllers;
+ protected TouchController mActiveController;
+ private TouchCompleteListener mTouchCompleteListener;
+
+ public BaseDragLayer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mActivity = (T) BaseActivity.fromContext(context);
+ }
+
+
+ public boolean isEventOverView(View view, MotionEvent ev) {
+ getDescendantRectRelativeToSelf(view, mHitRect);
+ return mHitRect.contains((int) ev.getX(), (int) ev.getY());
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ if (mTouchCompleteListener != null) {
+ mTouchCompleteListener.onTouchComplete();
+ }
+ mTouchCompleteListener = null;
+ } else if (action == MotionEvent.ACTION_DOWN) {
+ mActivity.finishAutoCancelActionMode();
+ }
+ return findActiveController(ev);
+ }
+
+ protected boolean findActiveController(MotionEvent ev) {
+ mActiveController = null;
+
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
+ mActiveController = topView;
+ return true;
+ }
+
+ for (TouchController controller : mControllers) {
+ if (controller.onControllerInterceptTouchEvent(ev)) {
+ mActiveController = controller;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ // Shortcuts can appear above folder
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null) {
+ if (child == topView) {
+ return super.onRequestSendAccessibilityEvent(child, event);
+ }
+ // Skip propagating onRequestSendAccessibilityEvent for all other children
+ // which are not topView
+ return false;
+ }
+ return super.onRequestSendAccessibilityEvent(child, event);
+ }
+
+ @Override
+ public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null) {
+ // Only add the top view as a child for accessibility when it is open
+ childrenForAccessibility.add(topView);
+ } else {
+ super.addChildrenForAccessibility(childrenForAccessibility);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ if (mTouchCompleteListener != null) {
+ mTouchCompleteListener.onTouchComplete();
+ }
+ mTouchCompleteListener = null;
+ }
+
+ if (mActiveController != null) {
+ return mActiveController.onControllerTouchEvent(ev);
+ } else {
+ // In case no child view handled the touch event, we may not get onIntercept anymore
+ return findActiveController(ev);
+ }
+ }
+
+ /**
+ * Determine the rect of the descendant in this DragLayer's coordinates
+ *
+ * @param descendant The descendant whose coordinates we want to find.
+ * @param r The rect into which to place the results.
+ * @return The factor by which this descendant is scaled relative to this DragLayer.
+ */
+ public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
+ mTmpXY[0] = 0;
+ mTmpXY[1] = 0;
+ float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
+
+ r.set(mTmpXY[0], mTmpXY[1],
+ (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()),
+ (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight()));
+ return scale;
+ }
+
+ public float getLocationInDragLayer(View child, int[] loc) {
+ loc[0] = 0;
+ loc[1] = 0;
+ return getDescendantCoordRelativeToSelf(child, loc);
+ }
+
+ public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
+ return getDescendantCoordRelativeToSelf(descendant, coord, false);
+ }
+
+ /**
+ * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's
+ * coordinates.
+ *
+ * @param descendant The descendant to which the passed coordinate is relative.
+ * @param coord The coordinate that we want mapped.
+ * @param includeRootScroll Whether or not to account for the scroll of the root descendant:
+ * sometimes this is relevant as in a child's coordinates within the root descendant.
+ * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
+ * this scale factor is assumed to be equal in X and Y, and so if at any point this
+ * assumption fails, we will need to return a pair of scale factors.
+ */
+ public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
+ boolean includeRootScroll) {
+ return Utilities.getDescendantCoordRelativeToAncestor(descendant, this,
+ coord, includeRootScroll);
+ }
+
+ /**
+ * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
+ */
+ public void mapCoordInSelfToDescendant(View descendant, int[] coord) {
+ Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
+ }
+
+ public void getViewRectRelativeToSelf(View v, Rect r) {
+ int[] loc = new int[2];
+ getLocationInWindow(loc);
+ int x = loc[0];
+ int y = loc[1];
+
+ v.getLocationInWindow(loc);
+ int vX = loc[0];
+ int vY = loc[1];
+
+ int left = vX - x;
+ int top = vY - y;
+ r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
+ }
+
+ @Override
+ public boolean dispatchUnhandledMove(View focused, int direction) {
+ // Consume the unhandled move if a container is open, to avoid switching pages underneath.
+ return AbstractFloatingView.getTopOpenView(mActivity) != null;
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null) {
+ return topView.requestFocus(direction, previouslyFocusedRect);
+ } else {
+ return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+ }
+ }
+
+ @Override
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null) {
+ topView.addFocusables(views, direction);
+ } else {
+ super.addFocusables(views, direction, focusableMode);
+ }
+ }
+
+ public void setTouchCompleteListener(TouchCompleteListener listener) {
+ mTouchCompleteListener = listener;
+ }
+
+ public interface TouchCompleteListener {
+ void onTouchComplete();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ // Override to allow type-checking of LayoutParams.
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return new LayoutParams(p);
+ }
+
+ public static class LayoutParams extends InsettableFrameLayout.LayoutParams {
+ public int x, y;
+ public boolean customPosition = false;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams lp) {
+ super(lp);
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public void setY(int y) {
+ this.y = y;
+ }
+
+ public int getY() {
+ return y;
+ }
+ }
+
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
+ if (flp instanceof LayoutParams) {
+ final LayoutParams lp = (LayoutParams) flp;
+ if (lp.customPosition) {
+ child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/HighlightableListView.java b/src/com/android/launcher3/views/HighlightableListView.java
new file mode 100644
index 0000000..7da979f
--- /dev/null
+++ b/src/com/android/launcher3/views/HighlightableListView.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.views;
+
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.v4.graphics.ColorUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.HeaderViewListAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+
+import java.util.ArrayList;
+
+/**
+ * Extension of list view with support for element highlighting.
+ */
+public class HighlightableListView extends ListView {
+
+ private int mPosHighlight = -1;
+ private boolean mColorAnimated = false;
+
+ public HighlightableListView(Context context) {
+ super(context);
+ }
+
+ public HighlightableListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public HighlightableListView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ super.setAdapter(new HighLightAdapter(adapter));
+ }
+
+ public void highlightPosition(int pos) {
+ if (mPosHighlight == pos) {
+ return;
+ }
+
+ mColorAnimated = false;
+ mPosHighlight = pos;
+ setSelection(mPosHighlight);
+
+ int start = getFirstVisiblePosition();
+ int end = getLastVisiblePosition();
+ if (start <= mPosHighlight && mPosHighlight <= end) {
+ highlightView(getChildAt(mPosHighlight - start));
+ }
+ }
+
+ private void highlightView(View view) {
+ if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
+ // already highlighted
+ } else {
+ view.setTag(R.id.view_highlighted, true);
+ view.setTag(R.id.view_unhighlight_background, view.getBackground());
+ view.setBackground(getHighlightBackground());
+ view.postDelayed(() -> {
+ mPosHighlight = -1;
+ unhighlightView(view);
+ }, 15000L);
+ }
+ }
+
+ private void unhighlightView(View view) {
+ if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
+ Object background = view.getTag(R.id.view_unhighlight_background);
+ if (background instanceof Drawable) {
+ view.setBackground((Drawable) background);
+ }
+ view.setTag(R.id.view_unhighlight_background, null);
+ view.setTag(R.id.view_highlighted, false);
+ }
+ }
+
+ private class HighLightAdapter extends HeaderViewListAdapter {
+ public HighLightAdapter(ListAdapter adapter) {
+ super(new ArrayList<>(), new ArrayList<>(), adapter);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = super.getView(position, convertView, parent);
+
+ if (position == mPosHighlight) {
+ highlightView(view);
+ } else {
+ unhighlightView(view);
+ }
+ return view;
+ }
+ }
+
+ private ColorDrawable getHighlightBackground() {
+ int color = ColorUtils.setAlphaComponent(Themes.getColorAccent(getContext()), 26);
+ if (mColorAnimated) {
+ return new ColorDrawable(color);
+ }
+ mColorAnimated = true;
+ ColorDrawable bg = new ColorDrawable(Color.WHITE);
+ ObjectAnimator anim = ObjectAnimator.ofInt(bg, "color", Color.WHITE, color);
+ anim.setEvaluator(new ArgbEvaluator());
+ anim.setDuration(200L);
+ anim.setRepeatMode(ValueAnimator.REVERSE);
+ anim.setRepeatCount(4);
+ anim.start();
+ return bg;
+ }
+}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 9d74218..12859c7 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -47,7 +47,7 @@
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
+import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
import java.util.ArrayList;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index a9694a7..1227dfe 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -25,7 +25,8 @@
public class UiFactory {
public static TouchController[] createTouchControllers(Launcher launcher) {
- return new TouchController[] {new AllAppsSwipeController(launcher)};
+ return new TouchController[] {
+ launcher.getDragController(), new AllAppsSwipeController(launcher)};
}
public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() {