summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3')
-rw-r--r--src/com/android/launcher3/AppWidgetsRestoredReceiver.java18
-rw-r--r--src/com/android/launcher3/BaseActivity.java6
-rw-r--r--src/com/android/launcher3/Launcher.java29
-rw-r--r--src/com/android/launcher3/LauncherAppState.java27
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetHost.java109
-rw-r--r--src/com/android/launcher3/LauncherModel.java3
-rw-r--r--src/com/android/launcher3/LauncherProvider.java2
-rw-r--r--src/com/android/launcher3/PagedView.java54
-rw-r--r--src/com/android/launcher3/SettingsActivity.java42
-rw-r--r--src/com/android/launcher3/Workspace.java10
-rw-r--r--src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java6
-rw-r--r--src/com/android/launcher3/allapps/AllAppsCaretController.java3
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerView.java92
-rw-r--r--src/com/android/launcher3/allapps/AllAppsTransitionController.java24
-rw-r--r--src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java4
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompat.java6
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java33
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java5
-rw-r--r--src/com/android/launcher3/config/BaseFlags.java73
-rw-r--r--src/com/android/launcher3/dragndrop/AddItemActivity.java28
-rw-r--r--src/com/android/launcher3/dragndrop/DragView.java323
-rw-r--r--src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java175
-rw-r--r--src/com/android/launcher3/folder/FolderIcon.java17
-rw-r--r--src/com/android/launcher3/folder/PreviewBackground.java22
-rw-r--r--src/com/android/launcher3/folder/PreviewItemManager.java5
-rw-r--r--src/com/android/launcher3/graphics/GradientView.java17
-rw-r--r--src/com/android/launcher3/logging/FileLog.java10
-rw-r--r--src/com/android/launcher3/logging/LoggerUtils.java8
-rw-r--r--src/com/android/launcher3/model/LoaderTask.java5
-rw-r--r--src/com/android/launcher3/notification/FlingAnimationUtils.java356
-rw-r--r--src/com/android/launcher3/notification/NotificationItemView.java22
-rw-r--r--src/com/android/launcher3/notification/NotificationListener.java40
-rw-r--r--src/com/android/launcher3/notification/NotificationMainView.java85
-rw-r--r--src/com/android/launcher3/notification/SwipeHelper.java687
-rw-r--r--src/com/android/launcher3/qsb/QsbContainerView.java9
-rw-r--r--src/com/android/launcher3/touch/OverScroll.java55
-rw-r--r--src/com/android/launcher3/touch/SwipeDetector.java (renamed from src/com/android/launcher3/allapps/VerticalPullDetector.java)188
-rw-r--r--src/com/android/launcher3/util/SettingsObserver.java100
-rw-r--r--src/com/android/launcher3/widget/WidgetAddFlowHandler.java18
-rw-r--r--src/com/android/launcher3/widget/WidgetsBottomSheet.java59
40 files changed, 1179 insertions, 1596 deletions
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 6e33d2a55..b249c9530 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -13,6 +13,7 @@ import android.support.annotation.WorkerThread;
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.ContentWriter;
@@ -26,7 +27,7 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) {
int hostId = intent.getIntExtra(AppWidgetManager.EXTRA_HOST_ID, 0);
Log.d(TAG, "Widget ID map received for host:" + hostId);
- if (hostId != Launcher.APPWIDGET_HOST_ID) {
+ if (hostId != LauncherAppWidgetHost.APPWIDGET_HOST_ID) {
return;
}
@@ -38,7 +39,8 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
.postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
- restoreAppWidgetIds(context, asyncResult, oldIds, newIds);
+ restoreAppWidgetIds(context, oldIds, newIds);
+ asyncResult.finish();
}
});
} else {
@@ -51,9 +53,13 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
* Updates the app widgets whose id has changed during the restore process.
*/
@WorkerThread
- static void restoreAppWidgetIds(Context context, PendingResult asyncResult,
- int[] oldWidgetIds, int[] newWidgetIds) {
- AppWidgetHost appWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
+ static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
+ AppWidgetHost appWidgetHost = new LauncherAppWidgetHost(context);
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ Log.e(TAG, "Skipping widget ID remap as widgets not supported");
+ appWidgetHost.deleteHost();
+ return;
+ }
if (!RestoreDbTask.isPending(context)) {
// Someone has already gone through our DB once, probably LoaderTask. Skip any further
// modifications of the DB.
@@ -62,7 +68,6 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
Log.d(TAG, "Deleting widgetId: " + widgetId);
appWidgetHost.deleteAppWidgetId(widgetId);
}
- asyncResult.finish();
return;
}
final ContentResolver cr = context.getContentResolver();
@@ -106,6 +111,5 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
if (app != null) {
app.getModel().forceReload();
}
- asyncResult.finish();
}
}
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 2b59ede47..e49649502 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -19,6 +19,7 @@ package com.android.launcher3;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.logging.UserEventDispatcher;
@@ -63,4 +64,9 @@ public abstract class BaseActivity extends Activity {
}
return mSystemUiController;
}
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3ae4316a9..7c258616f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -209,10 +209,8 @@ public class Launcher extends BaseActivity
private boolean mIsSafeModeEnabled;
- public static final int APPWIDGET_HOST_ID = 1024;
public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 500;
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
- private static final int ACTIVITY_START_DELAY = 1000;
// How long to wait before the new-shortcut animation automatically pans the workspace
private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
@@ -397,7 +395,10 @@ public class Launcher extends BaseActivity
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
- mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
+ mAppWidgetHost = new LauncherAppWidgetHost(this);
+ if (Utilities.ATLEAST_MARSHMALLOW) {
+ mAppWidgetHost.addProviderChangeListener(this);
+ }
mAppWidgetHost.startListening();
// If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
@@ -789,7 +790,7 @@ public class Launcher extends BaseActivity
}
@Override
- protected void onActivityResult(
+ public void onActivityResult(
final int requestCode, final int resultCode, final Intent data) {
handleActivityResult(requestCode, resultCode, data);
if (mLauncherCallbacks != null) {
@@ -1468,23 +1469,25 @@ public class Launcher extends BaseActivity
mWorkspace.addInScreen(view, info);
} else {
// Adding a shortcut to a Folder.
- final long folderIconId = container;
- FolderIcon folderIcon = (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View view) {
- return info != null && info.id == folderIconId;
- }
- });
-
+ FolderIcon folderIcon = findFolderIcon(container);
if (folderIcon != null) {
FolderInfo folderInfo = (FolderInfo) folderIcon.getTag();
folderInfo.add(info, args.rank, false);
} else {
- Log.e(TAG, "Could not find folder with id " + folderIconId + " to add shortcut.");
+ Log.e(TAG, "Could not find folder with id " + container + " to add shortcut.");
}
}
}
+ public FolderIcon findFolderIcon(final long folderIconId) {
+ return (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View view) {
+ return info != null && info.id == folderIconId;
+ }
+ });
+ }
+
/**
* Add a widget to the workspace.
*
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index cf20febd5..1ffe41bc6 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import android.content.ComponentName;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
@@ -28,13 +29,17 @@ import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dynamicui.ExtractionUtils;
+import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.SettingsObserver;
import com.android.launcher3.util.TestingUtils;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
+import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
+
public class LauncherAppState {
public static final boolean PROFILE_STARTUP = FeatureFlags.IS_DOGFOOD_BUILD;
@@ -47,7 +52,7 @@ public class LauncherAppState {
private final IconCache mIconCache;
private final WidgetPreviewLoader mWidgetCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
-
+ private final SettingsObserver mNotificationBadgingObserver;
public static LauncherAppState getInstance(final Context context) {
if (INSTANCE == null) {
@@ -117,6 +122,23 @@ public class LauncherAppState {
new ConfigMonitor(mContext).register();
ExtractionUtils.startColorExtractionServiceIfNecessary(mContext);
+
+ if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) {
+ mNotificationBadgingObserver = null;
+ } else {
+ // Register an observer to rebind the notification listener when badging is re-enabled.
+ mNotificationBadgingObserver = new SettingsObserver.Secure(
+ mContext.getContentResolver()) {
+ @Override
+ public void onSettingChanged(boolean isNotificationBadgingEnabled) {
+ if (isNotificationBadgingEnabled) {
+ NotificationListener.requestRebind(new ComponentName(
+ mContext, NotificationListener.class));
+ }
+ }
+ };
+ mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
+ }
}
/**
@@ -127,6 +149,9 @@ public class LauncherAppState {
final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
launcherApps.removeOnAppsChangedCallback(mModel);
PackageInstallerCompat.getInstance(mContext).onStop();
+ if (mNotificationBadgingObserver != null) {
+ mNotificationBadgingObserver.unregister();
+ }
}
LauncherModel setLauncher(Launcher launcher) {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 6e8c59b66..5573c5c15 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -16,12 +16,20 @@
package com.android.launcher3;
+import android.app.Activity;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
+import android.content.ActivityNotFoundException;
import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
import android.util.SparseArray;
import android.view.LayoutInflater;
+import android.widget.Toast;
+
+import com.android.launcher3.config.FeatureFlags;
import java.util.ArrayList;
@@ -33,14 +41,16 @@ import java.util.ArrayList;
*/
public class LauncherAppWidgetHost extends AppWidgetHost {
- private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
+ public static final int APPWIDGET_HOST_ID = 1024;
+
+ private final ArrayList<ProviderChangedListener> mProviderChangeListeners = new ArrayList<>();
private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
- private Launcher mLauncher;
+ private final Context mContext;
- public LauncherAppWidgetHost(Launcher launcher, int hostId) {
- super(launcher, hostId);
- mLauncher = launcher;
+ public LauncherAppWidgetHost(Context context) {
+ super(context, APPWIDGET_HOST_ID);
+ mContext = context;
}
@Override
@@ -53,6 +63,10 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
@Override
public void startListening() {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return;
+ }
+
try {
super.startListening();
} catch (Exception e) {
@@ -66,24 +80,38 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
}
}
- public void addProviderChangeListener(Runnable callback) {
+ @Override
+ public void stopListening() {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return;
+ }
+
+ super.stopListening();
+ }
+
+ @Override
+ public int allocateAppWidgetId() {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+
+ return super.allocateAppWidgetId();
+ }
+
+ public void addProviderChangeListener(ProviderChangedListener callback) {
mProviderChangeListeners.add(callback);
}
- public void removeProviderChangeListener(Runnable callback) {
+ public void removeProviderChangeListener(ProviderChangedListener callback) {
mProviderChangeListeners.remove(callback);
}
protected void onProvidersChanged() {
if (!mProviderChangeListeners.isEmpty()) {
- for (Runnable callback : new ArrayList<>(mProviderChangeListeners)) {
- callback.run();
+ for (ProviderChangedListener callback : new ArrayList<>(mProviderChangeListeners)) {
+ callback.notifyWidgetProvidersChanged();
}
}
-
- if (Utilities.ATLEAST_MARSHMALLOW) {
- mLauncher.notifyWidgetProvidersChanged();
- }
}
public AppWidgetHostView createView(Context context, int appWidgetId,
@@ -109,7 +137,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
// will update.
LauncherAppWidgetHostView view = mViews.get(appWidgetId);
if (view == null) {
- view = onCreateView(mLauncher, appWidgetId, appWidget);
+ view = onCreateView(mContext, appWidgetId, appWidget);
}
view.setAppWidget(appWidgetId, appWidget);
view.switchToErrorView();
@@ -124,11 +152,11 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
@Override
protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
- mLauncher, appWidget);
+ mContext, appWidget);
super.onProviderChanged(appWidgetId, info);
// The super method updates the dimensions of the providerInfo. Update the
// launcher spans accordingly.
- info.initSpans(mLauncher);
+ info.initSpans(mContext);
}
@Override
@@ -142,4 +170,53 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
super.clearViews();
mViews.clear();
}
+
+ public void startBindFlow(BaseActivity activity,
+ int appWidgetId, AppWidgetProviderInfo info, int requestCode) {
+
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ sendActionCancelled(activity, requestCode);
+ return;
+ }
+
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.provider)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, info.getProfile());
+ // TODO: we need to make sure that this accounts for the options bundle.
+ // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
+ activity.startActivityForResult(intent, requestCode);
+ }
+
+
+ public void startConfigActivity(BaseActivity activity, int widgetId, int requestCode) {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ sendActionCancelled(activity, requestCode);
+ return;
+ }
+
+ try {
+ startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
+ } catch (ActivityNotFoundException | SecurityException e) {
+ Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ sendActionCancelled(activity, requestCode);
+ }
+ }
+
+ private void sendActionCancelled(final BaseActivity activity, final int requestCode) {
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ activity.onActivityResult(requestCode, Activity.RESULT_CANCELED, null);
+ }
+ });
+ }
+
+ /**
+ * Listener for getting notifications on provider changes.
+ */
+ public interface ProviderChangedListener {
+
+ void notifyWidgetProvidersChanged();
+ }
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 82bee0e4c..f1638fda2 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -133,7 +133,7 @@ public class LauncherModel extends BroadcastReceiver
}
};
- public interface Callbacks {
+ public interface Callbacks extends LauncherAppWidgetHost.ProviderChangedListener {
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
public void clearPendingBinds();
@@ -159,7 +159,6 @@ public class LauncherModel extends BroadcastReceiver
HashSet<String> packageNames, HashSet<ComponentName> components,
UserHandle user);
public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
- public void notifyWidgetProvidersChanged();
public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets);
public void onPageBoundSynchronously(int page);
public void executeOnNextDraw(ViewOnDrawExecutor executor);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 4813571f5..dc83f36ad 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -1031,7 +1031,7 @@ public class LauncherProvider extends ContentProvider {
}
public AppWidgetHost newLauncherWidgetHost() {
- return new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID);
+ return new LauncherAppWidgetHost(mContext);
}
@Override
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 185c8879d..87f3ddaf4 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -48,6 +48,7 @@ import android.view.animation.Interpolator;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
@@ -68,10 +69,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
- // Overscroll constants
+ // OverScroll constants
private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
- private static final float OVERSCROLL_ACCELERATE_FACTOR = 2;
- private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
// The page is moved more than halfway, automatically move to the next page on touch up.
@@ -188,7 +187,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// Convenience/caching
private static final Matrix sTmpInvMatrix = new Matrix();
private static final float[] sTmpPoint = new float[2];
- private static final int[] sTmpIntPoint = new int[2];
private static final Rect sTmpRect = new Rect();
protected final Rect mInsets = new Rect();
@@ -233,8 +231,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
setOnHierarchyChangeListener(this);
setWillNotDraw(false);
-
- int edgeEffectColor = Themes.getAttrColor(getContext(), android.R.attr.colorEdgeEffect);
}
protected void setDefaultInterpolator(Interpolator interpolator) {
@@ -1305,29 +1301,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
}
- // This curve determines how the effect of scrolling over the limits of the page dimishes
- // as the user pulls further and further from the bounds
- private float overScrollInfluenceCurve(float f) {
- f -= 1.0f;
- return f * f * f + 1.0f;
- }
-
- protected float acceleratedOverFactor(float amount) {
- int screenSize = getViewportWidth();
-
- // We want to reach the max over scroll effect when the user has
- // over scrolled half the size of the screen
- float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize);
-
- if (Float.compare(f, 0f) == 0) return 0;
-
- // Clamp this factor, f, to -1 < f < 1
- if (Math.abs(f) >= 1) {
- f /= Math.abs(f);
- }
- return f;
- }
-
// While layout transitions are occurring, a child's position may stray from its baseline
// position. This method returns the magnitude of this stray at any given time.
public int getLayoutTransitionOffsetForPage(int index) {
@@ -1348,20 +1321,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected void dampedOverScroll(float amount) {
- int screenSize = getViewportWidth();
-
- float f = (amount / screenSize);
-
- if (Float.compare(f, 0f) == 0) return;
-
- f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+ if (Float.compare(amount, 0f) == 0) return;
- // Clamp this factor, f, to -1 < f < 1
- if (Math.abs(f) >= 1) {
- f /= Math.abs(f);
- }
-
- int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
+ int overScrollAmount = OverScroll.dampedScroll(amount, getViewportWidth());
if (amount < 0) {
mOverScrollX = overScrollAmount;
super.scrollTo(mOverScrollX, getScrollY());
@@ -1376,14 +1338,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
dampedOverScroll(amount);
}
- protected float maxOverScroll() {
- // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
- // exceed). Used to find out how much extra wallpaper we need for the over scroll effect
- float f = 1.0f;
- f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
- return OVERSCROLL_DAMP_FACTOR * f;
- }
-
/**
* return true if freescroll has been enabled, false otherwise
*/
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index fa7769e3f..90463725f 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -26,17 +26,15 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.database.ContentObserver;
import android.os.Bundle;
-import android.os.Handler;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.provider.Settings;
-import android.provider.Settings.System;
import com.android.launcher3.graphics.IconShapeOverride;
import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.util.SettingsObserver;
import com.android.launcher3.views.ButtonPreference;
/**
@@ -45,8 +43,8 @@ import com.android.launcher3.views.ButtonPreference;
public class SettingsActivity extends Activity {
private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
- // TODO: use Settings.Secure.NOTIFICATION_BADGING
- private static final String NOTIFICATION_BADGING = "notification_badging";
+ /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
+ public static final String NOTIFICATION_BADGING = "notification_badging";
/** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
@@ -88,12 +86,9 @@ public class SettingsActivity extends Activity {
// Register a content observer to listen for system setting changes while
// this UI is active.
- resolver.registerContentObserver(
- Settings.System.getUriFor(System.ACCELEROMETER_ROTATION),
- false, mRotationLockObserver);
+ mRotationLockObserver.register(Settings.System.ACCELEROMETER_ROTATION);
// Initialize the UI once
- mRotationLockObserver.onChange(true);
rotationPref.setDefaultValue(Utilities.getAllowRotationDefaultValue(getActivity()));
}
@@ -107,13 +102,7 @@ public class SettingsActivity extends Activity {
// Listen to system notification badge settings while this UI is active.
mIconBadgingObserver = new IconBadgingObserver(
iconBadgingPref, resolver, getFragmentManager());
- resolver.registerContentObserver(
- Settings.Secure.getUriFor(NOTIFICATION_BADGING),
- false, mIconBadgingObserver);
- resolver.registerContentObserver(
- Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS),
- false, mIconBadgingObserver);
- mIconBadgingObserver.onChange(true);
+ mIconBadgingObserver.register(NOTIFICATION_BADGING, NOTIFICATION_ENABLED_LISTENERS);
}
Preference iconShapeOverride = findPreference(IconShapeOverride.KEY_PREFERENCE);
@@ -129,11 +118,11 @@ public class SettingsActivity extends Activity {
@Override
public void onDestroy() {
if (mRotationLockObserver != null) {
- getActivity().getContentResolver().unregisterContentObserver(mRotationLockObserver);
+ mRotationLockObserver.unregister();
mRotationLockObserver = null;
}
if (mIconBadgingObserver != null) {
- getActivity().getContentResolver().unregisterContentObserver(mIconBadgingObserver);
+ mIconBadgingObserver.unregister();
mIconBadgingObserver = null;
}
super.onDestroy();
@@ -144,22 +133,18 @@ public class SettingsActivity extends Activity {
* Content observer which listens for system auto-rotate setting changes, and enables/disables
* the launcher rotation setting accordingly.
*/
- private static class SystemDisplayRotationLockObserver extends ContentObserver {
+ private static class SystemDisplayRotationLockObserver extends SettingsObserver.System {
private final Preference mRotationPref;
- private final ContentResolver mResolver;
public SystemDisplayRotationLockObserver(
Preference rotationPref, ContentResolver resolver) {
- super(new Handler());
+ super(resolver);
mRotationPref = rotationPref;
- mResolver = resolver;
}
@Override
- public void onChange(boolean selfChange) {
- boolean enabled = Settings.System.getInt(mResolver,
- Settings.System.ACCELEROMETER_ROTATION, 1) == 1;
+ public void onSettingChanged(boolean enabled) {
mRotationPref.setEnabled(enabled);
mRotationPref.setSummary(enabled
? R.string.allow_rotation_desc : R.string.allow_rotation_blocked_desc);
@@ -170,7 +155,7 @@ public class SettingsActivity extends Activity {
* Content observer which listens for system badging setting changes,
* and updates the launcher badging setting subtext accordingly.
*/
- private static class IconBadgingObserver extends ContentObserver
+ private static class IconBadgingObserver extends SettingsObserver.Secure
implements Preference.OnPreferenceClickListener {
private final ButtonPreference mBadgingPref;
@@ -179,15 +164,14 @@ public class SettingsActivity extends Activity {
public IconBadgingObserver(ButtonPreference badgingPref, ContentResolver resolver,
FragmentManager fragmentManager) {
- super(new Handler());
+ super(resolver);
mBadgingPref = badgingPref;
mResolver = resolver;
mFragmentManager = fragmentManager;
}
@Override
- public void onChange(boolean selfChange) {
- boolean enabled = Settings.Secure.getInt(mResolver, NOTIFICATION_BADGING, 1) == 1;
+ public void onSettingChanged(boolean enabled) {
int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off;
boolean serviceEnabled = true;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f781a3d1d..a2270d6c5 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -52,8 +52,10 @@ import android.view.accessibility.AccessibilityManager;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.Toast;
+
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.Launcher.LauncherOverlay;
+import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.UninstallDropTarget.DropTargetSource;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.OverviewAccessibilityDelegate;
@@ -86,6 +88,7 @@ import com.android.launcher3.util.VerticalFlingDetector;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -4079,7 +4082,7 @@ public class Workspace extends PagedView
* Used as a workaround to ensure that the AppWidgetService receives the
* PACKAGE_ADDED broadcast before updating widgets.
*/
- private class DeferredWidgetRefresh implements Runnable {
+ private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener {
private final ArrayList<LauncherAppWidgetInfo> mInfos;
private final LauncherAppWidgetHost mHost;
private final Handler mHandler;
@@ -4122,6 +4125,11 @@ public class Workspace extends PagedView
}
});
}
+
+ @Override
+ public void notifyWidgetProvidersChanged() {
+ run();
+ }
}
private class StateTransitionListener extends AnimatorListenerAdapter
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index 816121995..b7c500fa6 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -50,8 +50,7 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele
if ((host.getParent() instanceof DeepShortcutView)) {
info.addAction(mActions.get(ADD_TO_WORKSPACE));
} else if (host instanceof NotificationMainView) {
- NotificationMainView notificationView = (NotificationMainView) host;
- if (notificationView.canChildBeDismissed(notificationView)) {
+ if (((NotificationMainView) host).canChildBeDismissed()) {
info.addAction(mActions.get(DISMISS_NOTIFICATION));
}
}
@@ -88,8 +87,7 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele
if (!(host instanceof NotificationMainView)) {
return false;
}
- NotificationMainView notificationView = (NotificationMainView) host;
- notificationView.onChildDismissed(notificationView);
+ ((NotificationMainView) host).onChildDismissed();
announceConfirmation(R.string.notification_dismissed);
return true;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsCaretController.java b/src/com/android/launcher3/allapps/AllAppsCaretController.java
index 622322bfc..583b49f7b 100644
--- a/src/com/android/launcher3/allapps/AllAppsCaretController.java
+++ b/src/com/android/launcher3/allapps/AllAppsCaretController.java
@@ -22,13 +22,14 @@ import android.view.animation.Interpolator;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.pageindicators.CaretDrawable;
+import com.android.launcher3.touch.SwipeDetector;
public class AllAppsCaretController {
// Determines when the caret should flip. Should be accessed via getThreshold()
private static final float CARET_THRESHOLD = 0.015f;
private static final float CARET_THRESHOLD_LAND = 0.5f;
// The velocity at which the caret will peak (i.e. exhibit a 90 degree bend)
- private static final float PEAK_VELOCITY = VerticalPullDetector.RELEASE_VELOCITY_PX_MS * .7f;
+ private static final float PEAK_VELOCITY = SwipeDetector.RELEASE_VELOCITY_PX_MS * .7f;
private Launcher mLauncher;
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 701402b02..494cd5ac5 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -36,6 +36,8 @@ import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
+import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -60,7 +62,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
private SpringAnimationHandler mSpringAnimationHandler;
private OverScrollHelper mOverScrollHelper;
- private VerticalPullDetector mPullDetector;
+ private SwipeDetector mPullDetector;
private float mContentTranslationY = 0;
public static final Property<AllAppsRecyclerView, Float> CONTENT_TRANS_Y =
@@ -97,9 +99,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
R.dimen.all_apps_empty_search_bg_top_offset);
mOverScrollHelper = new OverScrollHelper();
- mPullDetector = new VerticalPullDetector(getContext());
- mPullDetector.setListener(mOverScrollHelper);
- mPullDetector.setDetectableScrollConditions(VerticalPullDetector.DIRECTION_BOTH, true);
+ mPullDetector = new SwipeDetector(getContext(), mOverScrollHelper, SwipeDetector.VERTICAL);
+ mPullDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, true);
}
public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) {
@@ -110,12 +111,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
}
@Override
- public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
- mPullDetector.onTouchEvent(ev);
- return super.onInterceptTouchEvent(rv, ev) || mOverScrollHelper.isInOverScroll();
- }
-
- @Override
public boolean onTouchEvent(MotionEvent e) {
mPullDetector.onTouchEvent(e);
if (FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null) {
@@ -204,8 +199,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
@Override
public void onDraw(Canvas c) {
- c.translate(0, mContentTranslationY);
-
// Draw the background
if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
mEmptySearchBackground.draw(c);
@@ -214,6 +207,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
super.onDraw(c);
}
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ canvas.translate(0, mContentTranslationY);
+ super.dispatchDraw(canvas);
+ canvas.translate(0, -mContentTranslationY);
+ }
+
public float getContentTranslationY() {
return mContentTranslationY;
}
@@ -281,7 +281,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
- boolean result = super.onInterceptTouchEvent(e);
+ mPullDetector.onTouchEvent(e);
+ boolean result = super.onInterceptTouchEvent(e) || mOverScrollHelper.isInOverScroll();
if (!result && e.getAction() == MotionEvent.ACTION_DOWN
&& mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
mEmptySearchBackground.setHotspot(e.getX(), e.getY());
@@ -338,6 +339,22 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter);
}
+ @Override
+ protected float getBottomFadingEdgeStrength() {
+ // No bottom fading edge.
+ return 0;
+ }
+
+ @Override
+ protected boolean isPaddingOffsetRequired() {
+ return true;
+ }
+
+ @Override
+ protected int getTopPaddingOffset() {
+ return -getPaddingTop();
+ }
+
/**
* Updates the bounds for the scrollbar.
*/
@@ -507,7 +524,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
}
}
- private class OverScrollHelper implements VerticalPullDetector.Listener {
+ private class OverScrollHelper implements SwipeDetector.Listener {
private static final float MAX_RELEASE_VELOCITY = 5000; // px / s
private static final float MAX_OVERSCROLL_PERCENTAGE = 0.07f;
@@ -540,11 +557,16 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
// and if one of the following criteria are met:
// - User scrolls down when they're already at the bottom.
// - User starts scrolling up, hits the top, and continues scrolling up.
+ boolean wasInOverScroll = mIsInOverScroll;
mIsInOverScroll = !mScrollbar.isDraggingThumb() &&
((!canScrollVertically(1) && displacement < 0) ||
(!canScrollVertically(-1) && isScrollingUp && mFirstScrollYOnScrollUp != 0));
- if (mIsInOverScroll) {
+ if (wasInOverScroll && !mIsInOverScroll) {
+ // Exit overscroll. This can happen when the user is in overscroll and then
+ // scrolls the opposite way.
+ reset(false /* shouldSpring */);
+ } else if (mIsInOverScroll) {
if (Float.compare(mFirstDisplacement, 0) == 0) {
// Because users can scroll before entering overscroll, we need to
// subtract the amount where the user was not in overscroll.
@@ -559,11 +581,15 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
@Override
public void onDragEnd(float velocity, boolean fling) {
+ reset(mIsInOverScroll /* shouldSpring */);
+ }
+
+ private void reset(boolean shouldSpring) {
float y = getContentTranslationY();
if (Float.compare(y, 0) != 0) {
- if (FeatureFlags.LAUNCHER3_PHYSICS) {
+ if (FeatureFlags.LAUNCHER3_PHYSICS && shouldSpring) {
// We calculate our own velocity to give the springs the desired effect.
- velocity = y / getDampedOverScroll(getHeight()) * MAX_RELEASE_VELOCITY;
+ float velocity = y / getDampedOverScroll(getHeight()) * MAX_RELEASE_VELOCITY;
// We want to negate the velocity because we are moving to 0 from -1 due to the
// downward motion. (y-axis -1 is above 0).
mSpringAnimationHandler.animateToPositionWithVelocity(0, -1, -velocity);
@@ -585,37 +611,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
}
private float getDampedOverScroll(float y) {
- return dampedOverScroll(y, getHeight()) * MAX_OVERSCROLL_PERCENTAGE;
- }
-
- /**
- * This curve determines how the effect of scrolling over the limits of the page diminishes
- * as the user pulls further and further from the bounds
- *
- * @param f The percentage of how much the user has overscrolled.
- * @return A transformed percentage based on the influence curve.
- */
- private float overScrollInfluenceCurve(float f) {
- f -= 1.0f;
- return f * f * f + 1.0f;
- }
-
- /**
- * @param amount The original amount overscrolled.
- * @param max The maximum amount that the View can overscroll.
- * @return The dampened overscroll amount.
- */
- private float dampedOverScroll(float amount, float max) {
- float f = amount / max;
- if (Float.compare(f, 0) == 0) return 0;
- f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
-
- // Clamp this factor, f, to -1 < f < 1
- if (Math.abs(f) >= 1) {
- f /= Math.abs(f);
- }
-
- return Math.round(f * max);
+ return OverScroll.dampedScroll(y, getHeight());
}
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index b85321e63..edfe0c15e 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -25,6 +25,7 @@ import com.android.launcher3.Workspace;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.GradientView;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.SystemUiController;
@@ -41,7 +42,7 @@ import com.android.launcher3.util.TouchController;
* If release velocity < THRES1, snap according to either top or bottom depending on whether it's
* closer to top or closer to the page indicator.
*/
-public class AllAppsTransitionController implements TouchController, VerticalPullDetector.Listener,
+public class AllAppsTransitionController implements TouchController, SwipeDetector.Listener,
SearchUiManager.OnScrollRangeChangeListener {
private static final String TAG = "AllAppsTrans";
@@ -51,8 +52,8 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f);
private final Interpolator mDecelInterpolator = new DecelerateInterpolator(3f);
private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
- private final VerticalPullDetector.ScrollInterpolator mScrollInterpolator
- = new VerticalPullDetector.ScrollInterpolator();
+ private final SwipeDetector.ScrollInterpolator mScrollInterpolator
+ = new SwipeDetector.ScrollInterpolator();
private static final float PARALLAX_COEFFICIENT = .125f;
private static final int SINGLE_FRAME_MS = 16;
@@ -68,7 +69,7 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
private float mStatusBarHeight;
private final Launcher mLauncher;
- private final VerticalPullDetector mDetector;
+ private final SwipeDetector mDetector;
private final ArgbEvaluator mEvaluator;
private final boolean mIsDarkTheme;
@@ -104,8 +105,7 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
- mDetector = new VerticalPullDetector(l);
- mDetector.setListener(this);
+ mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
mShiftRange = DEFAULT_SHIFT_RANGE;
mProgress = 1f;
@@ -134,17 +134,17 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
if (mDetector.isIdleState()) {
if (mLauncher.isAllAppsVisible()) {
- directionsToDetectScroll |= VerticalPullDetector.DIRECTION_DOWN;
+ directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE;
} else {
- directionsToDetectScroll |= VerticalPullDetector.DIRECTION_UP;
+ directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE;
}
} else {
if (isInDisallowRecatchBottomZone()) {
- directionsToDetectScroll |= VerticalPullDetector.DIRECTION_UP;
+ directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE;
} else if (isInDisallowRecatchTopZone()) {
- directionsToDetectScroll |= VerticalPullDetector.DIRECTION_DOWN;
+ directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE;
} else {
- directionsToDetectScroll |= VerticalPullDetector.DIRECTION_BOTH;
+ directionsToDetectScroll |= SwipeDetector.DIRECTION_BOTH;
ignoreSlopWhenSettling = true;
}
}
@@ -360,7 +360,7 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
}
private void calculateDuration(float velocity, float disp) {
- mAnimationDuration = mDetector.calculateDuration(velocity, disp / mShiftRange);
+ mAnimationDuration = SwipeDetector.calculateDuration(velocity, disp / mShiftRange);
}
public boolean animateToAllApps(AnimatorSet animationOut, long duration) {
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 21eb3fba0..26f6ec357 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -71,10 +71,6 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
return result;
}
- public static boolean matches(AppInfo info, String query) {
- return matches(info, query, StringMatcher.getInstance());
- }
-
public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
int queryLength = query.length();
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index 3efbbfba5..4e00eae9d 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -16,15 +16,12 @@
package com.android.launcher3.compat;
-import android.app.Activity;
-import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
@@ -76,9 +73,6 @@ public abstract class AppWidgetManagerCompat {
public abstract boolean bindAppWidgetIdIfAllowed(
int appWidgetId, AppWidgetProviderInfo info, Bundle options);
- public abstract void startConfigActivity(AppWidgetProviderInfo info, int widgetId,
- Activity activity, AppWidgetHost host, int requestCode);
-
public abstract LauncherAppWidgetProviderInfo findProvider(
ComponentName provider, UserHandle user);
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index f239f5c31..cb3bd6c7d 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -16,24 +16,21 @@
package com.android.launcher3.compat;
-import android.app.Activity;
-import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetProviderInfo;
-import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.annotation.Nullable;
-import android.widget.Toast;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -49,6 +46,9 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
@Override
public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return Collections.emptyList();
+ }
if (packageUser == null) {
ArrayList<AppWidgetProviderInfo> providers = new ArrayList<AppWidgetProviderInfo>();
for (UserHandle user : mUserManager.getUserProfiles()) {
@@ -71,24 +71,20 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
@Override
public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
Bundle options) {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return false;
+ }
return mAppWidgetManager.bindAppWidgetIdIfAllowed(
appWidgetId, info.getProfile(), info.provider, options);
}
@Override
- public void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity,
- AppWidgetHost host, int requestCode) {
- try {
- host.startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
- } catch (ActivityNotFoundException | SecurityException e) {
- Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- }
- }
-
- @Override
public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandle user) {
- for (AppWidgetProviderInfo info : mAppWidgetManager
- .getInstalledProvidersForProfile(user)) {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return null;
+ }
+ for (AppWidgetProviderInfo info :
+ getAllProviders(new PackageUserKey(provider.getPackageName(), user))) {
if (info.provider.equals(provider)) {
return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
}
@@ -99,6 +95,9 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
@Override
public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return result;
+ }
for (UserHandle user : mUserManager.getUserProfiles()) {
for (AppWidgetProviderInfo info :
mAppWidgetManager.getInstalledProvidersForProfile(user)) {
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
index 1c48a13bd..44158edbf 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
@@ -20,8 +20,10 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.support.annotation.Nullable;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PackageUserKey;
+import java.util.Collections;
import java.util.List;
class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL {
@@ -32,6 +34,9 @@ class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL {
@Override
public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return Collections.emptyList();
+ }
if (packageUser == null) {
return super.getAllProviders(null);
}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
new file mode 100644
index 000000000..7964dd15d
--- /dev/null
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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.config;
+
+/**
+ * Defines a set of flags used to control various launcher behaviors.
+ *
+ * All the flags should be defined here with appropriate default values. To override a value,
+ * redefine it in {@link FeatureFlags}.
+ *
+ * This class is kept package-private to prevent direct access.
+ */
+abstract class BaseFlags {
+
+ BaseFlags() {}
+
+ public static final boolean IS_DOGFOOD_BUILD = false;
+
+ // Custom flags go below this
+ public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false;
+ public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
+ public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
+ public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
+ public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true;
+ // When enabled allows to use any point on the fast scrollbar to start dragging.
+ public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
+ // When enabled while all-apps open, the soft input will be set to adjust resize .
+ public static final boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false;
+ // When enabled the promise icon is visible in all apps while installation an app.
+ public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
+ // When enabled uses the AllAppsRadialGradientAndScrimDrawable for all apps
+ public static final boolean LAUNCHER3_GRADIENT_ALL_APPS = true;
+ // When enabled allows use of physics based motions in the Launcher.
+ public static final boolean LAUNCHER3_PHYSICS = true;
+ // When enabled allows use of spring motions on the icons.
+ public static final boolean LAUNCHER3_SPRING_ICONS = true;
+
+ // Feature flag to enable moving the QSB on the 0th screen of the workspace.
+ public static final boolean QSB_ON_FIRST_SCREEN = true;
+ // When enabled the all-apps icon is not added to the hotseat.
+ public static final boolean NO_ALL_APPS_ICON = true;
+ // When enabled fling down gesture on the first workspace triggers search.
+ public static final boolean PULLDOWN_SEARCH = false;
+ // When enabled the status bar may show dark icons based on the top of the wallpaper.
+ public static final boolean LIGHT_STATUS_BAR = false;
+ // When enabled icons are badged with the number of notifications associated with that app.
+ public static final boolean BADGE_ICONS = true;
+ // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}.
+ public static final boolean LEGACY_ICON_TREATMENT = true;
+ // When enabled, adaptive icons would have shadows baked when being stored to icon cache.
+ public static final boolean ADAPTIVE_ICON_SHADOW = true;
+ // When enabled, app discovery will be enabled if service is implemented
+ public static final boolean DISCOVERY_ENABLED = false;
+ // When enabled, the qsb will be moved to the hotseat.
+ public static final boolean QSB_IN_HOTSEAT = true;
+
+ // Features to control Launcher3Go behavior
+ public static final boolean GO_DISABLE_WIDGETS = false;
+}
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 01893bdaf..c843e7266 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -16,14 +16,8 @@
package com.android.launcher3.dragndrop;
-import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
-
import android.annotation.TargetApi;
import android.app.ActivityOptions;
-import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -45,8 +39,8 @@ import android.view.View.OnTouchListener;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -61,6 +55,11 @@ import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView;
+import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
+import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+
@TargetApi(Build.VERSION_CODES.O)
public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
@@ -78,7 +77,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
private LivePreviewWidgetCell mWidgetCell;
// Widget request specific options.
- private AppWidgetHost mAppWidgetHost;
+ private LauncherAppWidgetHost mAppWidgetHost;
private AppWidgetManagerCompat mAppWidgetManager;
private PendingAddWidgetInfo mPendingWidgetInfo;
private int mPendingBindWidgetId;
@@ -212,7 +211,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
mWidgetCell.setPreview(PinItemDragListener.getPreview(mRequest));
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
- mAppWidgetHost = new AppWidgetHost(this, Launcher.APPWIDGET_HOST_ID);
+ mAppWidgetHost = new LauncherAppWidgetHost(this);
mPendingWidgetInfo = new PendingAddWidgetInfo(widgetInfo);
mPendingWidgetInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
@@ -256,13 +255,8 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
}
// request bind widget
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER,
- mPendingWidgetInfo.componentName);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE,
- mRequest.getAppWidgetProviderInfo(this).getProfile());
- startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
+ mAppWidgetHost.startBindFlow(this, mPendingBindWidgetId,
+ mRequest.getAppWidgetProviderInfo(this), REQUEST_BIND_APPWIDGET);
}
private void acceptWidget(int widgetId) {
@@ -280,7 +274,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
}
@Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_BIND_APPWIDGET) {
int widgetId = data != null
? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId)
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 09cfc1eac..b6e38bb15 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -16,23 +16,18 @@
package com.android.launcher3.dragndrop;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.FloatArrayEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.pm.LauncherActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
@@ -44,13 +39,11 @@ import android.graphics.drawable.InsetDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.support.animation.DynamicAnimation;
+import android.support.animation.FloatPropertyCompat;
import android.support.animation.SpringAnimation;
import android.support.animation.SpringForce;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfo;
@@ -76,12 +69,16 @@ import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.Arrays;
import java.util.List;
-public class DragView extends FrameLayout {
+public class DragView extends View {
+ private static final ColorMatrix sTempMatrix1 = new ColorMatrix();
+ private static final ColorMatrix sTempMatrix2 = new ColorMatrix();
+
public static final int COLOR_CHANGE_DURATION = 120;
public static final int VIEW_ZOOM_DURATION = 150;
@Thunk static float sDragAlpha = 1f;
+ private boolean mDrawBitmap = true;
private Bitmap mBitmap;
private Bitmap mCrossFadeBitmap;
@Thunk Paint mPaint;
@@ -114,16 +111,11 @@ public class DragView extends FrameLayout {
private int mAnimatedShiftY;
// Below variable only needed IF FeatureFlags.LAUNCHER3_SPRING_ICONS is {@code true}
- private SpringAnimation mSpringX, mSpringY;
- private ImageView mFgImageView, mBgImageView;
+ private Drawable mBgSpringDrawable, mFgSpringDrawable;
+ private SpringFloatValue mTranslateX, mTranslateY;
private Path mScaledMaskPath;
private Drawable mBadge;
-
- // Following three values are fine tuned with motion ux designer
- private final static int STIFFNESS = 4000;
- private final static float DAMPENING_RATIO = 1f;
- private final static int PARALLAX_MAX_IN_DP = 8;
- private final int mDelta;
+ private ColorMatrixColorFilter mBaseFilter;
/**
* Construct the drag view.
@@ -193,12 +185,11 @@ public class DragView extends FrameLayout {
mBlurSizeOutline = getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
setElevation(getResources().getDimension(R.dimen.drag_elevation));
- setWillNotDraw(false);
- mDelta = (int)(getResources().getDisplayMetrics().density * PARALLAX_MAX_IN_DP);
}
/**
- * Initialize {@code #mIconDrawable} only if the icon type is app icon (not shortcut or folder).
+ * Initialize {@code #mIconDrawable} if the item can be represented using
+ * an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}.
*/
@TargetApi(Build.VERSION_CODES.O)
public void setItemInfo(final ItemInfo info) {
@@ -206,7 +197,8 @@ public class DragView extends FrameLayout {
return;
}
if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
- info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
+ info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
return;
}
// Load the adaptive icon on a background thread and add the view in ui thread.
@@ -216,51 +208,103 @@ public class DragView extends FrameLayout {
public void run() {
LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
Object[] outObj = new Object[1];
- Drawable dr = getFullDrawable(info, appState, outObj);
+ final Drawable dr = getFullDrawable(info, appState, outObj);
if (dr instanceof AdaptiveIconDrawable) {
int w = mBitmap.getWidth();
int h = mBitmap.getHeight();
+ int blurMargin = (int) mLauncher.getResources()
+ .getDimension(R.dimen.blur_size_medium_outline) / 2;
+
+ Rect bounds = new Rect(0, 0, w, h);
+ bounds.inset(blurMargin, blurMargin);
+ Utilities.scaleRectAboutCenter(bounds,
+ IconNormalizer.getInstance(mLauncher).getScale(dr, null, null, null));
AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
- float blurSizeOutline = mLauncher.getResources()
- .getDimension(R.dimen.blur_size_medium_outline);
- float normalizationScale = IconNormalizer.getInstance(mLauncher)
- .getScale(adaptiveIcon, null, null, null) * ((w - blurSizeOutline) / w);
- adaptiveIcon.setBounds(0, 0, w, h);
-
- final Path mask = getMaskPath(adaptiveIcon, normalizationScale);
- mFgImageView = setupImageView(adaptiveIcon.getForeground(), normalizationScale);
- mBgImageView = setupImageView(adaptiveIcon.getBackground(), normalizationScale);
- mSpringX = setupSpringAnimation(-w/4, w/4, DynamicAnimation.TRANSLATION_X);
- mSpringY = setupSpringAnimation(-h/4, h/4, DynamicAnimation.TRANSLATION_Y);
+
+ // Shrink very tiny bit so that the clip path is smaller than the original bitmap
+ // that has anti aliased edges and shadows.
+ Rect shrunkBounds = new Rect(bounds);
+ Utilities.scaleRectAboutCenter(shrunkBounds, 0.98f);
+ adaptiveIcon.setBounds(shrunkBounds);
+ final Path mask = adaptiveIcon.getIconMask();
+
+ mTranslateX = new SpringFloatValue(DragView.this,
+ w * AdaptiveIconDrawable.getExtraInsetFraction());
+ mTranslateY = new SpringFloatValue(DragView.this,
+ h * AdaptiveIconDrawable.getExtraInsetFraction());
mBadge = getBadge(info, appState, outObj[0]);
- int blurMargin = (int) blurSizeOutline / 2;
- mBadge.setBounds(blurMargin, blurMargin, w - blurMargin, h - blurMargin);
+ mBadge.setBounds(bounds);
+
+ bounds.inset(
+ (int) (-bounds.width() * AdaptiveIconDrawable.getExtraInsetFraction()),
+ (int) (-bounds.height() * AdaptiveIconDrawable.getExtraInsetFraction())
+ );
+ mBgSpringDrawable = adaptiveIcon.getBackground();
+ if (mBgSpringDrawable == null) {
+ mBgSpringDrawable = new ColorDrawable(Color.TRANSPARENT);
+ }
+ mBgSpringDrawable.setBounds(bounds);
+ mFgSpringDrawable = adaptiveIcon.getForeground();
+ if (mFgSpringDrawable == null) {
+ mFgSpringDrawable = new ColorDrawable(Color.TRANSPARENT);
+ }
+ mFgSpringDrawable.setBounds(bounds);
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// Assign the variable on the UI thread to avoid race conditions.
mScaledMaskPath = mask;
- addView(mBgImageView);
- addView(mFgImageView);
- setWillNotDraw(true);
+
+ // Do not draw the background in case of folder as its translucent
+ mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);
if (info.isDisabled()) {
FastBitmapDrawable d = new FastBitmapDrawable(null);
d.setIsDisabled(true);
- ColorFilter cf = d.getColorFilter();
- mBgImageView.setColorFilter(cf);
- mFgImageView.setColorFilter(cf);
- mBadge.setColorFilter(cf);
+ mBaseFilter = (ColorMatrixColorFilter) d.getColorFilter();
}
+ updateColorFilter();
}
});
}
}});
}
+ @TargetApi(Build.VERSION_CODES.O)
+ private void updateColorFilter() {
+ if (mCurrentFilter == null) {
+ mPaint.setColorFilter(null);
+
+ if (mScaledMaskPath != null) {
+ mBgSpringDrawable.setColorFilter(mBaseFilter);
+ mBgSpringDrawable.setColorFilter(mBaseFilter);
+ mBadge.setColorFilter(mBaseFilter);
+ }
+ } else {
+ ColorMatrixColorFilter currentFilter = new ColorMatrixColorFilter(mCurrentFilter);
+ mPaint.setColorFilter(currentFilter);
+
+ if (mScaledMaskPath != null) {
+ if (mBaseFilter != null) {
+ mBaseFilter.getColorMatrix(sTempMatrix1);
+ sTempMatrix2.set(mCurrentFilter);
+ sTempMatrix1.postConcat(sTempMatrix2);
+
+ currentFilter = new ColorMatrixColorFilter(sTempMatrix1);
+ }
+
+ mBgSpringDrawable.setColorFilter(currentFilter);
+ mFgSpringDrawable.setColorFilter(currentFilter);
+ mBadge.setColorFilter(currentFilter);
+ }
+ }
+
+ invalidate();
+ }
+
/**
* Returns the full drawable for {@param info}.
* @param outObj this is set to the internal data associated with {@param info},
@@ -291,6 +335,14 @@ public class DragView extends FrameLayout {
return sm.getShortcutIconDrawable(si.get(0),
appState.getInvariantDeviceProfile().fillResIconDpi);
}
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon(
+ mLauncher, info.id, new Point(mBitmap.getWidth(), mBitmap.getHeight()));
+ if (icon == null) {
+ return null;
+ }
+ outObj[0] = icon;
+ return icon;
} else {
return null;
}
@@ -318,84 +370,17 @@ public class DragView extends FrameLayout {
float insetFraction = (iconSize - badgeSize) / iconSize;
return new InsetDrawable(new FastBitmapDrawable(badge),
insetFraction, insetFraction, 0, 0);
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ return ((FolderAdaptiveIcon) obj).getBadge();
} else {
return mLauncher.getPackageManager()
.getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
}
}
- private ImageView setupImageView(Drawable drawable, float normalizationScale) {
- FrameLayout.LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
- ImageView imageViewOut = new ImageView(getContext());
- imageViewOut.setLayoutParams(params);
- imageViewOut.setScaleType(ImageView.ScaleType.FIT_XY);
- imageViewOut.setScaleX(normalizationScale);
- imageViewOut.setScaleY(normalizationScale);
- imageViewOut.setImageDrawable(drawable);
- return imageViewOut;
- }
-
- private SpringAnimation setupSpringAnimation(int minValue, int maxValue,
- DynamicAnimation.ViewProperty property) {
- SpringAnimation s = new SpringAnimation(mFgImageView, property, 0);
- s.setMinValue(minValue).setMaxValue(maxValue);
- s.setSpring(new SpringForce(0)
- .setDampingRatio(DAMPENING_RATIO)
- .setStiffness(STIFFNESS));
- return s;
- }
-
- @TargetApi(Build.VERSION_CODES.O)
- private Path getMaskPath(AdaptiveIconDrawable dr, float normalizationScale) {
- Matrix m = new Matrix();
- // Shrink very tiny bit so that the clip path is smaller than the original bitmap
- // that has anti aliased edges and shadows.
- float s = normalizationScale * .97f;
- m.setScale(s, s, dr.getBounds().centerX(), dr.getBounds().centerY());
- Path p = new Path();
- dr.getIconMask().transform(m, p);
- return p;
- }
-
- private void applySpring(int x, int y) {
- if (mSpringX == null || mSpringY == null) {
- return;
- }
- mSpringX.animateToFinalPosition(Utilities.boundToRange(x, -mDelta, mDelta));
- mSpringY.animateToFinalPosition(Utilities.boundToRange(y, -mDelta, mDelta));
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int w = right - left;
- int h = bottom - top;
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).layout(-w / 4, -h / 4, w + w / 4, h + h / 4);
- }
- }
-
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int w = mBitmap.getWidth();
- int h = mBitmap.getHeight();
- setMeasuredDimension(w, h);
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).measure(w, h);
- }
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- if (mScaledMaskPath != null) {
- int cnt = canvas.save();
- canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
- canvas.clipPath(mScaledMaskPath);
- super.dispatchDraw(canvas);
- canvas.restoreToCount(cnt);
- mBadge.draw(canvas);
- } else {
- super.dispatchDraw(canvas);
- }
+ setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
}
/** Sets the scale of the view over the normal workspace icon size. */
@@ -439,43 +424,37 @@ public class DragView extends FrameLayout {
return mDragRegion;
}
- // Draws drag shadow for system DND.
- @SuppressLint("WrongCall")
- public void drawDragShadow(Canvas canvas) {
- final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.scale(getScaleX(), getScaleY());
- onDraw(canvas);
- canvas.restoreToCount(saveCount);
- }
-
- // Provides drag shadow metrics for system DND.
- public void provideDragShadowMetrics(Point size, Point touch) {
- size.set((int)(mBitmap.getWidth() * getScaleX()), (int)(mBitmap.getHeight() * getScaleY()));
-
- final float xGrowth = mBitmap.getWidth() * (getScaleX() - 1);
- final float yGrowth = mBitmap.getHeight() * (getScaleY() - 1);
- touch.set(
- mRegistrationX + (int)Math.round(xGrowth / 2),
- mRegistrationY + (int)Math.round(yGrowth / 2));
- }
-
@Override
protected void onDraw(Canvas canvas) {
mHasDrawn = true;
- boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
- if (crossFade) {
- int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
- mPaint.setAlpha(alpha);
+
+ if (mDrawBitmap) {
+ // Always draw the bitmap to mask anti aliasing due to clipPath
+ boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
+ if (crossFade) {
+ int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
+ mPaint.setAlpha(alpha);
+ }
+ canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
+ if (crossFade) {
+ mPaint.setAlpha((int) (255 * mCrossFadeProgress));
+ final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
+ float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
+ canvas.scale(sX, sY);
+ canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
+ canvas.restoreToCount(saveCount);
+ }
}
- canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
- if (crossFade) {
- mPaint.setAlpha((int) (255 * mCrossFadeProgress));
- final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
- float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
- float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
- canvas.scale(sX, sY);
- canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
- canvas.restoreToCount(saveCount);
+
+ if (mScaledMaskPath != null) {
+ int cnt = canvas.save();
+ canvas.clipPath(mScaledMaskPath);
+ mBgSpringDrawable.draw(canvas);
+ canvas.translate(mTranslateX.mValue, mTranslateY.mValue);
+ mFgSpringDrawable.draw(canvas);
+ canvas.restoreToCount(cnt);
+ mBadge.draw(canvas);
}
}
@@ -512,8 +491,7 @@ public class DragView extends FrameLayout {
animateFilterTo(m1.getArray());
} else {
if (mCurrentFilter == null) {
- mPaint.setColorFilter(null);
- invalidate();
+ updateColorFilter();
} else {
animateFilterTo(new ColorMatrix().getArray());
}
@@ -534,8 +512,7 @@ public class DragView extends FrameLayout {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- mPaint.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
- invalidate();
+ updateColorFilter();
}
});
mFilterAnimator.start();
@@ -590,8 +567,10 @@ public class DragView extends FrameLayout {
* @param touchY the y coordinate the user touched in DragLayer coordinates
*/
public void move(int touchX, int touchY) {
- if (touchX > 0 && touchY > 0 && mLastTouchX > 0 && mLastTouchY > 0) {
- applySpring(mLastTouchX - touchX, mLastTouchY - touchY);
+ if (touchX > 0 && touchY > 0 && mLastTouchX > 0 && mLastTouchY > 0
+ && mScaledMaskPath != null) {
+ mTranslateX.animateToPos(mLastTouchX - touchX);
+ mTranslateY.animateToPos(mLastTouchY - touchY);
}
mLastTouchX = touchX;
mLastTouchY = touchY;
@@ -642,6 +621,48 @@ public class DragView extends FrameLayout {
return mInitialScale;
}
+ private static class SpringFloatValue {
+
+ private static final FloatPropertyCompat<SpringFloatValue> VALUE =
+ new FloatPropertyCompat<SpringFloatValue>("value") {
+ @Override
+ public float getValue(SpringFloatValue object) {
+ return object.mValue;
+ }
+
+ @Override
+ public void setValue(SpringFloatValue object, float value) {
+ object.mValue = value;
+ object.mView.invalidate();
+ }
+ };
+
+ // Following three values are fine tuned with motion ux designer
+ private final static int STIFFNESS = 4000;
+ private final static float DAMPENING_RATIO = 1f;
+ private final static int PARALLAX_MAX_IN_DP = 8;
+
+ private final View mView;
+ private final SpringAnimation mSpring;
+ private final float mDelta;
+
+ private float mValue;
+
+ public SpringFloatValue(View view, float range) {
+ mView = view;
+ mSpring = new SpringAnimation(this, VALUE, 0)
+ .setMinValue(-range).setMaxValue(range)
+ .setSpring(new SpringForce(0)
+ .setDampingRatio(DAMPENING_RATIO)
+ .setStiffness(STIFFNESS));
+ mDelta = view.getResources().getDisplayMetrics().density * PARALLAX_MAX_IN_DP;
+ }
+
+ public void animateToPos(float value) {
+ mSpring.animateToFinalPosition(Utilities.boundToRange(value, -mDelta, mDelta));
+ }
+ }
+
private static class FixedSizeEmptyDrawable extends ColorDrawable {
private final int mSize;
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
new file mode 100644
index 000000000..c90546088
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 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.dragndrop;
+
+import android.annotation.TargetApi;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.Log;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.PreviewBackground;
+import com.android.launcher3.util.Preconditions;
+
+import java.util.concurrent.Callable;
+
+/**
+ * {@link AdaptiveIconDrawable} representation of a {@link FolderIcon}
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class FolderAdaptiveIcon extends AdaptiveIconDrawable {
+ private static final String TAG = "FolderAdaptiveIcon";
+
+ private final Drawable mBadge;
+ private final Path mMask;
+
+ private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) {
+ super(bg, fg);
+ mBadge = badge;
+ mMask = mask;
+ }
+
+ @Override
+ public Path getIconMask() {
+ return mMask;
+ }
+
+ public Drawable getBadge() {
+ return mBadge;
+ }
+
+ public static FolderAdaptiveIcon createFolderAdaptiveIcon(
+ final Launcher launcher, final long folderId, Point dragViewSize) {
+ Preconditions.assertNonUiThread();
+ int margin = launcher.getResources()
+ .getDimensionPixelSize(R.dimen.blur_size_medium_outline);
+
+ // Allocate various bitmaps on the background thread, because why not!
+ final Bitmap badge = Bitmap.createBitmap(
+ dragViewSize.x - margin, dragViewSize.y - margin, Bitmap.Config.ARGB_8888);
+
+ // The bitmap for the preview is generated larger than needed to allow for the spring effect
+ float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
+ final Bitmap preview = Bitmap.createBitmap(
+ (int) (dragViewSize.x * sizeScaleFactor), (int) (dragViewSize.y * sizeScaleFactor),
+ Bitmap.Config.ARGB_8888);
+
+ // Create the actual drawable on the UI thread to avoid race conditions with
+ // FolderIcon draw pass
+ try {
+ return new MainThreadExecutor().submit(new Callable<FolderAdaptiveIcon>() {
+ @Override
+ public FolderAdaptiveIcon call() throws Exception {
+ FolderIcon icon = launcher.findFolderIcon(folderId);
+ return icon == null ? null : createDrawableOnUiThread(icon, badge, preview);
+ }
+ }).get();
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to create folder icon", e);
+ return null;
+ }
+ }
+
+ /**
+ * Initializes various bitmaps on the UI thread and returns the final drawable.
+ */
+ private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
+ Bitmap badgeBitmap, Bitmap previewBitmap) {
+ Preconditions.assertUIThread();
+ float margin = icon.getResources().getDimension(R.dimen.blur_size_medium_outline) / 2;
+
+ Canvas c = new Canvas();
+ PreviewBackground bg = icon.getFolderBackground();
+
+ // Initialize badge
+ c.setBitmap(badgeBitmap);
+ bg.drawShadow(c);
+ bg.drawBackgroundStroke(c);
+ icon.drawBadge(c);
+
+ // Initialize preview
+ float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() /
+ (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
+ float previewShiftX = shiftFactor * previewBitmap.getWidth();
+ float previewShiftY = shiftFactor * previewBitmap.getHeight();
+
+ c.setBitmap(previewBitmap);
+ c.translate(previewShiftX, previewShiftY);
+ icon.getPreviewItemManager().draw(c);
+ c.setBitmap(null);
+
+ // Initialize mask
+ Path mask = new Path();
+ Matrix m = new Matrix();
+ m.setTranslate(margin, margin);
+ bg.getClipPath().transform(m, mask);
+
+ ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBitmap, margin, margin);
+ ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap,
+ margin - previewShiftX, margin - previewShiftY);
+
+ return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
+ }
+
+ /**
+ * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds
+ */
+ private static class ShiftedBitmapDrawable extends Drawable {
+
+ private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+ private final Bitmap mBitmap;
+ private final float mShiftX;
+ private final float mShiftY;
+
+ ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
+ mBitmap = bitmap;
+ mShiftX = shiftX;
+ mShiftY = shiftY;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
+ }
+
+ @Override
+ public void setAlpha(int i) { }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mPaint.setColorFilter(colorFilter);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 84ec18410..6533b0463 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -440,6 +440,14 @@ public class FolderIcon extends FrameLayout implements FolderListener {
invalidate();
}
+ public PreviewBackground getFolderBackground() {
+ return mBackground;
+ }
+
+ public PreviewItemManager getPreviewItemManager() {
+ return mPreviewItemManager;
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
@@ -463,14 +471,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
} else {
saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
if (mPreviewLayoutRule.clipToBackground()) {
- mBackground.clipCanvasSoftware(canvas, Region.Op.INTERSECT);
+ canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT);
}
}
- // The items are drawn in coordinates relative to the preview offset
- canvas.translate(mBackground.basePreviewOffsetX, mBackground.basePreviewOffsetY);
mPreviewItemManager.draw(canvas);
- canvas.translate(-mBackground.basePreviewOffsetX, -mBackground.basePreviewOffsetY);
if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) {
mBackground.clipCanvasHardware(canvas);
@@ -481,6 +486,10 @@ public class FolderIcon extends FrameLayout implements FolderListener {
mBackground.drawBackgroundStroke(canvas);
}
+ drawBadge(canvas);
+ }
+
+ public void drawBadge(Canvas canvas) {
if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) {
int offsetX = mBackground.getOffsetX();
int offsetY = mBackground.getOffsetY();
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 61490eee8..eba5d984d 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -195,19 +195,28 @@ public class PreviewBackground {
invalidate();
}
+ public int getBgColor() {
+ int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+ return ColorUtils.setAlphaComponent(mBgColor, alpha);
+ }
+
public void drawBackground(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
- int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
- mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, alpha));
+ mPaint.setColor(getBgColor());
drawCircle(canvas, 0 /* deltaRadius */);
- // Draw shadow.
+ drawShadow(canvas);
+ }
+
+ public void drawShadow(Canvas canvas) {
if (mShadowShader == null) {
return;
}
+
float radius = getScaledRadius();
float shadowRadius = radius + mStrokeWidth;
+ mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLACK);
int offsetX = getOffsetX();
int offsetY = getOffsetY();
@@ -219,7 +228,7 @@ public class PreviewBackground {
} else {
saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
- clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
+ canvas.clipPath(getClipPath(), Region.Op.DIFFERENCE);
}
mShaderMatrix.setScale(shadowRadius, shadowRadius);
@@ -295,12 +304,11 @@ public class PreviewBackground {
radius - deltaRadius, mPaint);
}
- // It is the callers responsibility to save and restore the canvas layers.
- void clipCanvasSoftware(Canvas canvas, Region.Op op) {
+ public Path getClipPath() {
mPath.reset();
float r = getScaledRadius();
mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
- canvas.clipPath(mPath, op);
+ return mPath;
}
// It is the callers responsibility to save and restore the canvas layers.
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 2ecb54ca8..2d979a661 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -146,6 +146,10 @@ public class PreviewItemManager {
}
public void draw(Canvas canvas) {
+ // The items are drawn in coordinates relative to the preview offset
+ PreviewBackground bg = mIcon.getFolderBackground();
+ canvas.translate(bg.basePreviewOffsetX, bg.basePreviewOffsetY);
+
float firstPageItemsTransX = 0;
if (mShouldSlideInFirstPage) {
drawParams(canvas, mCurrentPageParams, mCurrentPageItemsTransX);
@@ -154,6 +158,7 @@ public class PreviewItemManager {
}
drawParams(canvas, mFirstPageParams, firstPageItemsTransX);
+ canvas.translate(-bg.basePreviewOffsetX, -bg.basePreviewOffsetY);
}
public void onParamsChanged() {
diff --git a/src/com/android/launcher3/graphics/GradientView.java b/src/com/android/launcher3/graphics/GradientView.java
index 678396df1..5455b43ec 100644
--- a/src/com/android/launcher3/graphics/GradientView.java
+++ b/src/com/android/launcher3/graphics/GradientView.java
@@ -27,6 +27,7 @@ import android.graphics.RectF;
import android.graphics.Shader;
import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
@@ -46,7 +47,6 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis
private static final int DEFAULT_COLOR = Color.WHITE;
private static final int ALPHA_MASK_HEIGHT_DP = 500;
private static final int ALPHA_MASK_WIDTH_DP = 2;
- private static final int ALPHA_COLORS = 0xBF;
private static final boolean DEBUG = false;
private final Bitmap mAlphaGradientMask;
@@ -62,7 +62,7 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis
private final Paint mPaintNoScrim = new Paint();
private float mProgress;
private final int mMaskHeight, mMaskWidth;
- private final Context mAppContext;
+ private final int mAlphaColors;
private final Paint mDebugPaint = DEBUG ? new Paint() : null;
private final Interpolator mAccelerator = new AccelerateInterpolator();
private final float mAlphaStart;
@@ -71,15 +71,14 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis
public GradientView(Context context, AttributeSet attrs) {
super(context, attrs);
- this.mAppContext = context.getApplicationContext();
- this.mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP,
- mAppContext.getResources().getDisplayMetrics());
- this.mMaskWidth = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP,
- mAppContext.getResources().getDisplayMetrics());
+ DisplayMetrics dm = getResources().getDisplayMetrics();
+ this.mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm);
+ this.mMaskWidth = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
Launcher launcher = Launcher.getLauncher(context);
this.mAlphaStart = launcher.getDeviceProfile().isVerticalBarLayout() ? 0 : 100;
this.mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
this.mWallpaperColorInfo = WallpaperColorInfo.getInstance(launcher);
+ mAlphaColors = getResources().getInteger(R.integer.extracted_color_gradient_alpha);
updateColors();
mAlphaGradientMask = createDitheredAlphaMask();
}
@@ -104,9 +103,9 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis
private void updateColors() {
this.mColor1 = ColorUtils.setAlphaComponent(mWallpaperColorInfo.getMainColor(),
- ALPHA_COLORS);
+ mAlphaColors);
this.mColor2 = ColorUtils.setAlphaComponent(mWallpaperColorInfo.getSecondaryColor(),
- ALPHA_COLORS);
+ mAlphaColors);
if (mWidth + mHeight > 0) {
createRadialShader();
}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 4c83e9ac2..f7f8ef18f 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -29,6 +29,8 @@ import java.util.concurrent.TimeUnit;
*/
public final class FileLog {
+ protected static final boolean ENABLED =
+ FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE;
private static final String FILE_NAME_PREFIX = "log-";
private static final DateFormat DATE_FORMAT =
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
@@ -39,7 +41,7 @@ public final class FileLog {
private static File sLogsDirectory = null;
public static void setDir(File logsDir) {
- if (FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) {
+ if (ENABLED) {
synchronized (DATE_FORMAT) {
// If the target directory changes, stop any active thread.
if (sHandler != null && !logsDir.equals(sLogsDirectory)) {
@@ -76,7 +78,7 @@ public final class FileLog {
}
public static void print(String tag, String msg, Exception e) {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!ENABLED) {
return;
}
String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg);
@@ -102,7 +104,7 @@ public final class FileLog {
* @param out if not null, all the persisted logs are copied to the writer.
*/
public static void flushAll(PrintWriter out) throws InterruptedException {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!ENABLED) {
return;
}
CountDownLatch latch = new CountDownLatch(1);
@@ -135,7 +137,7 @@ public final class FileLog {
@Override
public boolean handleMessage(Message msg) {
- if (sLogsDirectory == null || !FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (sLogsDirectory == null || !ENABLED) {
return true;
}
switch (msg.what) {
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 72a083b9e..ebb69c43b 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -25,6 +25,7 @@ import com.android.launcher3.InfoDropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.UninstallDropTarget;
+import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
@@ -159,6 +160,13 @@ public class LoggerUtils {
return t;
}
+ public static Target newTarget(int targetType, TargetExtension extension) {
+ Target t = new Target();
+ t.type = targetType;
+ t.extension = extension;
+ return t;
+ }
+
public static Target newTarget(int targetType) {
Target t = new Target();
t.type = targetType;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 5bad436f8..c56325ad5 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -548,6 +548,11 @@ public class LoaderTask implements Runnable {
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ c.markDeleted("Only legacy shortcuts can have null package");
+ continue;
+ }
+ // Follow through
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
// Read all Launcher-specific widget details
boolean customWidget = c.itemType ==
diff --git a/src/com/android/launcher3/notification/FlingAnimationUtils.java b/src/com/android/launcher3/notification/FlingAnimationUtils.java
deleted file mode 100644
index a1f7e49c0..000000000
--- a/src/com/android/launcher3/notification/FlingAnimationUtils.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2017 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.notification;
-
-import android.animation.Animator;
-import android.content.Context;
-import android.view.ViewPropertyAnimator;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-/**
- * Utility class to calculate general fling animation when the finger is released.
- *
- * This class was copied from com.android.systemui.statusbar.
- */
-public class FlingAnimationUtils {
-
- private static final float LINEAR_OUT_SLOW_IN_X2 = 0.35f;
- private static final float LINEAR_OUT_SLOW_IN_X2_MAX = 0.68f;
- private static final float LINEAR_OUT_FASTER_IN_X2 = 0.5f;
- private static final float LINEAR_OUT_FASTER_IN_Y2_MIN = 0.4f;
- private static final float LINEAR_OUT_FASTER_IN_Y2_MAX = 0.5f;
- private static final float MIN_VELOCITY_DP_PER_SECOND = 250;
- private static final float HIGH_VELOCITY_DP_PER_SECOND = 3000;
-
- private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 0.75f;
- private final float mSpeedUpFactor;
- private final float mY2;
-
- private float mMinVelocityPxPerSecond;
- private float mMaxLengthSeconds;
- private float mHighVelocityPxPerSecond;
- private float mLinearOutSlowInX2;
-
- private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
- private PathInterpolator mInterpolator;
- private float mCachedStartGradient = -1;
- private float mCachedVelocityFactor = -1;
-
- public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
- this(ctx, maxLengthSeconds, 0.0f);
- }
-
- /**
- * @param maxLengthSeconds the longest duration an animation can become in seconds
- * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards
- * the end of the animation. 0 means it's at the beginning and no
- * acceleration will take place.
- */
- public FlingAnimationUtils(Context ctx, float maxLengthSeconds, float speedUpFactor) {
- this(ctx, maxLengthSeconds, speedUpFactor, -1.0f, 1.0f);
- }
-
- /**
- * @param maxLengthSeconds the longest duration an animation can become in seconds
- * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards
- * the end of the animation. 0 means it's at the beginning and no
- * acceleration will take place.
- * @param x2 the x value to take for the second point of the bezier spline. If a value below 0
- * is provided, the value is automatically calculated.
- * @param y2 the y value to take for the second point of the bezier spline
- */
- public FlingAnimationUtils(Context ctx, float maxLengthSeconds, float speedUpFactor, float x2,
- float y2) {
- mMaxLengthSeconds = maxLengthSeconds;
- mSpeedUpFactor = speedUpFactor;
- if (x2 < 0) {
- mLinearOutSlowInX2 = interpolate(LINEAR_OUT_SLOW_IN_X2,
- LINEAR_OUT_SLOW_IN_X2_MAX,
- mSpeedUpFactor);
- } else {
- mLinearOutSlowInX2 = x2;
- }
- mY2 = y2;
-
- mMinVelocityPxPerSecond
- = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
- mHighVelocityPxPerSecond
- = HIGH_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
- }
-
- private static float interpolate(float start, float end, float amount) {
- return start * (1.0f - amount) + end * amount;
- }
-
- /**
- * Applies the interpolator and length to the animator, such that the fling animation is
- * consistent with the finger motion.
- *
- * @param animator the animator to apply
- * @param currValue the current value
- * @param endValue the end value of the animator
- * @param velocity the current velocity of the motion
- */
- public void apply(Animator animator, float currValue, float endValue, float velocity) {
- apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
- }
-
- /**
- * Applies the interpolator and length to the animator, such that the fling animation is
- * consistent with the finger motion.
- *
- * @param animator the animator to apply
- * @param currValue the current value
- * @param endValue the end value of the animator
- * @param velocity the current velocity of the motion
- */
- public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
- float velocity) {
- apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
- }
-
- /**
- * Applies the interpolator and length to the animator, such that the fling animation is
- * consistent with the finger motion.
- *
- * @param animator the animator to apply
- * @param currValue the current value
- * @param endValue the end value of the animator
- * @param velocity the current velocity of the motion
- * @param maxDistance the maximum distance for this interaction; the maximum animation length
- * gets multiplied by the ratio between the actual distance and this value
- */
- public void apply(Animator animator, float currValue, float endValue, float velocity,
- float maxDistance) {
- AnimatorProperties properties = getProperties(currValue, endValue, velocity,
- maxDistance);
- animator.setDuration(properties.duration);
- animator.setInterpolator(properties.interpolator);
- }
-
- /**
- * Applies the interpolator and length to the animator, such that the fling animation is
- * consistent with the finger motion.
- *
- * @param animator the animator to apply
- * @param currValue the current value
- * @param endValue the end value of the animator
- * @param velocity the current velocity of the motion
- * @param maxDistance the maximum distance for this interaction; the maximum animation length
- * gets multiplied by the ratio between the actual distance and this value
- */
- public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
- float velocity, float maxDistance) {
- AnimatorProperties properties = getProperties(currValue, endValue, velocity,
- maxDistance);
- animator.setDuration(properties.duration);
- animator.setInterpolator(properties.interpolator);
- }
-
- private AnimatorProperties getProperties(float currValue,
- float endValue, float velocity, float maxDistance) {
- float maxLengthSeconds = (float) (mMaxLengthSeconds
- * Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
- float diff = Math.abs(endValue - currValue);
- float velAbs = Math.abs(velocity);
- float velocityFactor = mSpeedUpFactor == 0.0f
- ? 1.0f : Math.min(velAbs / HIGH_VELOCITY_DP_PER_SECOND, 1.0f);
- float startGradient = interpolate(LINEAR_OUT_SLOW_IN_START_GRADIENT,
- mY2 / mLinearOutSlowInX2, velocityFactor);
- float durationSeconds = startGradient * diff / velAbs;
- Interpolator slowInInterpolator = getInterpolator(startGradient, velocityFactor);
- if (durationSeconds <= maxLengthSeconds) {
- mAnimatorProperties.interpolator = slowInInterpolator;
- } else if (velAbs >= mMinVelocityPxPerSecond) {
-
- // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
- durationSeconds = maxLengthSeconds;
- VelocityInterpolator velocityInterpolator
- = new VelocityInterpolator(durationSeconds, velAbs, diff);
- InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
- velocityInterpolator, slowInInterpolator, Interpolators.LINEAR_OUT_SLOW_IN);
- mAnimatorProperties.interpolator = superInterpolator;
- } else {
-
- // Just use a normal interpolator which doesn't take the velocity into account.
- durationSeconds = maxLengthSeconds;
- mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN;
- }
- mAnimatorProperties.duration = (long) (durationSeconds * 1000);
- return mAnimatorProperties;
- }
-
- private Interpolator getInterpolator(float startGradient, float velocityFactor) {
- if (startGradient != mCachedStartGradient
- || velocityFactor != mCachedVelocityFactor) {
- float speedup = mSpeedUpFactor * (1.0f - velocityFactor);
- mInterpolator = new PathInterpolator(speedup,
- speedup * startGradient,
- mLinearOutSlowInX2, mY2);
- mCachedStartGradient = startGradient;
- mCachedVelocityFactor = velocityFactor;
- }
- return mInterpolator;
- }
-
- /**
- * Applies the interpolator and length to the animator, such that the fling animation is
- * consistent with the finger motion for the case when the animation is making something
- * disappear.
- *
- * @param animator the animator to apply
- * @param currValue the current value
- * @param endValue the end value of the animator
- * @param velocity the current velocity of the motion
- * @param maxDistance the maximum distance for this interaction; the maximum animation length
- * gets multiplied by the ratio between the actual distance and this value
- */
- public void applyDismissing(Animator animator, float currValue, float endValue,
- float velocity, float maxDistance) {
- AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
- maxDistance);
- animator.setDuration(properties.duration);
- animator.setInterpolator(properties.interpolator);
- }
-
- /**
- * Applies the interpolator and length to the animator, such that the fling animation is
- * consistent with the finger motion for the case when the animation is making something
- * disappear.
- *
- * @param animator the animator to apply
- * @param currValue the current value
- * @param endValue the end value of the animator
- * @param velocity the current velocity of the motion
- * @param maxDistance the maximum distance for this interaction; the maximum animation length
- * gets multiplied by the ratio between the actual distance and this value
- */
- public void applyDismissing(ViewPropertyAnimator animator, float currValue, float endValue,
- float velocity, float maxDistance) {
- AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
- maxDistance);
- animator.setDuration(properties.duration);
- animator.setInterpolator(properties.interpolator);
- }
-
- private AnimatorProperties getDismissingProperties(float currValue, float endValue,
- float velocity, float maxDistance) {
- float maxLengthSeconds = (float) (mMaxLengthSeconds
- * Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
- float diff = Math.abs(endValue - currValue);
- float velAbs = Math.abs(velocity);
- float y2 = calculateLinearOutFasterInY2(velAbs);
-
- float startGradient = y2 / LINEAR_OUT_FASTER_IN_X2;
- Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, LINEAR_OUT_FASTER_IN_X2, y2);
- float durationSeconds = startGradient * diff / velAbs;
- if (durationSeconds <= maxLengthSeconds) {
- mAnimatorProperties.interpolator = mLinearOutFasterIn;
- } else if (velAbs >= mMinVelocityPxPerSecond) {
-
- // Cross fade between linear-out-faster-in and linear interpolator with current
- // velocity.
- durationSeconds = maxLengthSeconds;
- VelocityInterpolator velocityInterpolator
- = new VelocityInterpolator(durationSeconds, velAbs, diff);
- InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
- velocityInterpolator, mLinearOutFasterIn, Interpolators.LINEAR_OUT_SLOW_IN);
- mAnimatorProperties.interpolator = superInterpolator;
- } else {
-
- // Just use a normal interpolator which doesn't take the velocity into account.
- durationSeconds = maxLengthSeconds;
- mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN;
- }
- mAnimatorProperties.duration = (long) (durationSeconds * 1000);
- return mAnimatorProperties;
- }
-
- /**
- * Calculates the y2 control point for a linear-out-faster-in path interpolator depending on the
- * velocity. The faster the velocity, the more "linear" the interpolator gets.
- *
- * @param velocity the velocity of the gesture.
- * @return the y2 control point for a cubic bezier path interpolator
- */
- private float calculateLinearOutFasterInY2(float velocity) {
- float t = (velocity - mMinVelocityPxPerSecond)
- / (mHighVelocityPxPerSecond - mMinVelocityPxPerSecond);
- t = Math.max(0, Math.min(1, t));
- return (1 - t) * LINEAR_OUT_FASTER_IN_Y2_MIN + t * LINEAR_OUT_FASTER_IN_Y2_MAX;
- }
-
- /**
- * @return the minimum velocity a gesture needs to have to be considered a fling
- */
- public float getMinVelocityPxPerSecond() {
- return mMinVelocityPxPerSecond;
- }
-
- /**
- * An interpolator which interpolates two interpolators with an interpolator.
- */
- private static final class InterpolatorInterpolator implements Interpolator {
-
- private Interpolator mInterpolator1;
- private Interpolator mInterpolator2;
- private Interpolator mCrossfader;
-
- InterpolatorInterpolator(Interpolator interpolator1, Interpolator interpolator2,
- Interpolator crossfader) {
- mInterpolator1 = interpolator1;
- mInterpolator2 = interpolator2;
- mCrossfader = crossfader;
- }
-
- @Override
- public float getInterpolation(float input) {
- float t = mCrossfader.getInterpolation(input);
- return (1 - t) * mInterpolator1.getInterpolation(input)
- + t * mInterpolator2.getInterpolation(input);
- }
- }
-
- /**
- * An interpolator which interpolates with a fixed velocity.
- */
- private static final class VelocityInterpolator implements Interpolator {
-
- private float mDurationSeconds;
- private float mVelocity;
- private float mDiff;
-
- private VelocityInterpolator(float durationSeconds, float velocity, float diff) {
- mDurationSeconds = durationSeconds;
- mVelocity = velocity;
- mDiff = diff;
- }
-
- @Override
- public float getInterpolation(float input) {
- float time = input * mDurationSeconds;
- return time * mVelocity / mDiff;
- }
- }
-
- private static class AnimatorProperties {
- Interpolator interpolator;
- long duration;
- }
-
-}
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 11f6aa081..78c64d7da 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -37,6 +37,7 @@ import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
import com.android.launcher3.popup.PopupItemView;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Themes;
@@ -56,7 +57,7 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
private TextView mHeaderCount;
private NotificationMainView mMainView;
private NotificationFooterLayout mFooter;
- private SwipeHelper mSwipeHelper;
+ private SwipeDetector mSwipeDetector;
private boolean mAnimatingNextIcon;
private int mNotificationHeaderTextColor = Notification.COLOR_DEFAULT;
@@ -75,12 +76,14 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mHeaderText = (TextView) findViewById(R.id.notification_text);
- mHeaderCount = (TextView) findViewById(R.id.notification_count);
- mMainView = (NotificationMainView) findViewById(R.id.main_view);
- mFooter = (NotificationFooterLayout) findViewById(R.id.footer);
- mSwipeHelper = new SwipeHelper(SwipeHelper.X, mMainView, getContext());
- mSwipeHelper.setDisableHardwareLayers(true);
+ mHeaderText = findViewById(R.id.notification_text);
+ mHeaderCount = findViewById(R.id.notification_count);
+ mMainView = findViewById(R.id.main_view);
+ mFooter = findViewById(R.id.footer);
+
+ mSwipeDetector = new SwipeDetector(getContext(), mMainView, SwipeDetector.HORIZONTAL);
+ mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
+ mMainView.setSwipeDetector(mSwipeDetector);
}
public NotificationMainView getMainView() {
@@ -136,7 +139,8 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
return false;
}
getParent().requestDisallowInterceptTouchEvent(true);
- return mSwipeHelper.onInterceptTouchEvent(ev);
+ mSwipeDetector.onTouchEvent(ev);
+ return mSwipeDetector.isDraggingOrSettling();
}
@Override
@@ -145,7 +149,7 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
// The notification hasn't been populated yet.
return false;
}
- return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev);
+ return mSwipeDetector.onTouchEvent(ev) || super.onTouchEvent(ev);
}
public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) {
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 73d89aa18..6a7098915 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -30,15 +30,20 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
+
import com.android.launcher3.LauncherModel;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.SettingsObserver;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
+
/**
* A {@link NotificationListenerService} that sends updates to its
* {@link NotificationsChangedListener} when notifications are posted or canceled,
@@ -57,12 +62,14 @@ public class NotificationListener extends NotificationListenerService {
private static NotificationListener sNotificationListenerInstance = null;
private static NotificationsChangedListener sNotificationsChangedListener;
private static boolean sIsConnected;
+ private static boolean sIsCreated;
private final Handler mWorkerHandler;
private final Handler mUiHandler;
-
private final Ranking mTempRanking = new Ranking();
+ private SettingsObserver mNotificationBadgingObserver;
+
private final Handler.Callback mWorkerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
@@ -77,7 +84,7 @@ public class NotificationListener extends NotificationListenerService {
List<StatusBarNotification> activeNotifications;
if (sIsConnected) {
try {
- activeNotifications = filterNotifications(getActiveNotifications());
+ activeNotifications = filterNotifications(getActiveNotifications());
} catch (SecurityException ex) {
Log.e(TAG, "SecurityException: failed to fetch notifications");
activeNotifications = new ArrayList<StatusBarNotification>();
@@ -130,6 +137,28 @@ public class NotificationListener extends NotificationListenerService {
sNotificationListenerInstance = this;
}
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ sIsCreated = true;
+ mNotificationBadgingObserver = new SettingsObserver.Secure(getContentResolver()) {
+ @Override
+ public void onSettingChanged(boolean isNotificationBadgingEnabled) {
+ if (!isNotificationBadgingEnabled) {
+ requestUnbind();
+ }
+ }
+ };
+ mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ sIsCreated = false;
+ mNotificationBadgingObserver.unregister();
+ }
+
public static @Nullable NotificationListener getInstanceIfConnected() {
return sIsConnected ? sNotificationListenerInstance : null;
}
@@ -143,6 +172,11 @@ public class NotificationListener extends NotificationListenerService {
NotificationListener notificationListener = getInstanceIfConnected();
if (notificationListener != null) {
notificationListener.onNotificationFullRefresh();
+ } else if (!sIsCreated && sNotificationsChangedListener != null) {
+ // User turned off badging globally, so we unbound this service;
+ // tell the listener that there are no notifications to remove dots.
+ sNotificationsChangedListener.onNotificationFullRefresh(
+ Collections.<StatusBarNotification>emptyList());
}
}
@@ -205,7 +239,7 @@ public class NotificationListener extends NotificationListenerService {
.getActiveNotifications(NotificationKeyData.extractKeysOnly(keys)
.toArray(new String[keys.size()]));
return notifications == null
- ? Collections.<StatusBarNotification>emptyList() : Arrays.asList(notifications);
+ ? Collections.<StatusBarNotification>emptyList() : Arrays.asList(notifications);
}
/**
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 9b8dd648f..5aff28db4 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -23,15 +23,17 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.RippleDrawable;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Themes;
@@ -39,7 +41,7 @@ import com.android.launcher3.util.Themes;
* A {@link android.widget.FrameLayout} that contains a single notification,
* e.g. icon + title + text.
*/
-public class NotificationMainView extends FrameLayout implements SwipeHelper.Callback {
+public class NotificationMainView extends FrameLayout implements SwipeDetector.Listener {
private NotificationInfo mNotificationInfo;
private ViewGroup mTextAndBackground;
@@ -47,6 +49,8 @@ public class NotificationMainView extends FrameLayout implements SwipeHelper.Cal
private TextView mTitleView;
private TextView mTextView;
+ private SwipeDetector mSwipeDetector;
+
public NotificationMainView(Context context) {
this(context, null, 0);
}
@@ -78,6 +82,10 @@ public class NotificationMainView extends FrameLayout implements SwipeHelper.Cal
applyNotificationInfo(mainNotification, iconView, false);
}
+ public void setSwipeDetector(SwipeDetector swipeDetector) {
+ mSwipeDetector = swipeDetector;
+ }
+
/**
* Sets the content of this view, animating it after a new icon shifts up if necessary.
*/
@@ -113,29 +121,11 @@ public class NotificationMainView extends FrameLayout implements SwipeHelper.Cal
}
- // SwipeHelper.Callback's
-
- @Override
- public View getChildAtPosition(MotionEvent ev) {
- return this;
- }
-
- @Override
- public boolean canChildBeDismissed(View v) {
+ public boolean canChildBeDismissed() {
return mNotificationInfo != null && mNotificationInfo.dismissable;
}
- @Override
- public boolean isAntiFalsingNeeded() {
- return false;
- }
-
- @Override
- public void onBeginDrag(View v) {
- }
-
- @Override
- public void onChildDismissed(View v) {
+ public void onChildDismissed() {
Launcher launcher = Launcher.getLauncher(getContext());
launcher.getPopupDataProvider().cancelNotification(
mNotificationInfo.notificationKey);
@@ -145,22 +135,55 @@ public class NotificationMainView extends FrameLayout implements SwipeHelper.Cal
LauncherLogProto.ItemType.NOTIFICATION);
}
+ // SwipeDetector.Listener's
@Override
- public void onDragCancelled(View v) {
- }
+ public void onDragStart(boolean start) { }
- @Override
- public void onChildSnappedBack(View animView, float targetLeft) {
- }
@Override
- public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
- // Don't fade out.
+ public boolean onDrag(float displacement, float velocity) {
+ setTranslationX(canChildBeDismissed()
+ ? displacement : OverScroll.dampedScroll(displacement, getWidth()));
+ animate().cancel();
return true;
}
@Override
- public float getFalsingThresholdFactor() {
- return 1;
+ public void onDragEnd(float velocity, boolean fling) {
+ final boolean willExit;
+ final float endTranslation;
+
+ if (!canChildBeDismissed()) {
+ willExit = false;
+ endTranslation = 0;
+ } else if (fling) {
+ willExit = true;
+ endTranslation = velocity < 0 ? - getWidth() : getWidth();
+ } else if (Math.abs(getTranslationX()) > getWidth() / 2) {
+ willExit = true;
+ endTranslation = (getTranslationX() < 0 ? -getWidth() : getWidth());
+ } else {
+ willExit = false;
+ endTranslation = 0;
+ }
+
+ SwipeDetector.ScrollInterpolator interpolator = new SwipeDetector.ScrollInterpolator();
+ interpolator.setVelocityAtZero(velocity);
+
+ long duration = SwipeDetector.calculateDuration(velocity,
+ (endTranslation - getTranslationX()) / getWidth());
+ animate()
+ .setDuration(duration)
+ .setInterpolator(interpolator)
+ .translationX(endTranslation)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mSwipeDetector.finishedScrolling();
+ if (willExit) {
+ onChildDismissed();
+ }
+ }
+ }).start();
}
}
diff --git a/src/com/android/launcher3/notification/SwipeHelper.java b/src/com/android/launcher3/notification/SwipeHelper.java
deleted file mode 100644
index ebbe5fc6a..000000000
--- a/src/com/android/launcher3/notification/SwipeHelper.java
+++ /dev/null
@@ -1,687 +0,0 @@
-/*
- * Copyright (C) 2017 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.notification;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.RectF;
-import android.os.Handler;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-import com.android.launcher3.R;
-
-/**
- * This class was copied from com.android.systemui.
- */
-public class SwipeHelper {
- private static final String TAG = "SwipeHelper";
- private static final boolean DEBUG_INVALIDATE = false;
- private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
- private static final boolean CONSTRAIN_SWIPE = true;
- private static final boolean FADE_OUT_DURING_SWIPE = true;
- private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
-
- public static final int X = 0;
- public static final int Y = 1;
-
- private static final float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
- private static final int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
- private static final int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
- private static final int MAX_DISMISS_VELOCITY = 4000; // dp/sec
- private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
-
- static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width
- // beyond which swipe progress->0
- private float mMinSwipeProgress = 0f;
- private float mMaxSwipeProgress = 1f;
-
- private final FlingAnimationUtils mFlingAnimationUtils;
- private float mPagingTouchSlop;
- private final Callback mCallback;
- private final Handler mHandler;
- private final int mSwipeDirection;
- private final VelocityTracker mVelocityTracker;
-
- private float mInitialTouchPos;
- private float mPerpendicularInitialTouchPos;
- private boolean mDragging;
- private boolean mSnappingChild;
- private View mCurrView;
- private boolean mCanCurrViewBeDimissed;
- private float mDensityScale;
- private float mTranslation = 0;
-
- private boolean mLongPressSent;
- private LongPressListener mLongPressListener;
- private Runnable mWatchLongPress;
- private final long mLongPressTimeout;
-
- final private int[] mTmpPos = new int[2];
- private final int mFalsingThreshold;
- private boolean mTouchAboveFalsingThreshold;
- private boolean mDisableHwLayers;
-
- private final ArrayMap<View, Animator> mDismissPendingMap = new ArrayMap<>();
-
- public SwipeHelper(int swipeDirection, Callback callback, Context context) {
- mCallback = callback;
- mHandler = new Handler();
- mSwipeDirection = swipeDirection;
- mVelocityTracker = VelocityTracker.obtain();
- mDensityScale = context.getResources().getDisplayMetrics().density;
- mPagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
-
- mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); // extra long-press!
- mFalsingThreshold = context.getResources().getDimensionPixelSize(
- R.dimen.swipe_helper_falsing_threshold);
- mFlingAnimationUtils = new FlingAnimationUtils(context, getMaxEscapeAnimDuration() / 1000f);
- }
-
- public void setLongPressListener(LongPressListener listener) {
- mLongPressListener = listener;
- }
-
- public void setDensityScale(float densityScale) {
- mDensityScale = densityScale;
- }
-
- public void setPagingTouchSlop(float pagingTouchSlop) {
- mPagingTouchSlop = pagingTouchSlop;
- }
-
- public void setDisableHardwareLayers(boolean disableHwLayers) {
- mDisableHwLayers = disableHwLayers;
- }
-
- private float getPos(MotionEvent ev) {
- return mSwipeDirection == X ? ev.getX() : ev.getY();
- }
-
- private float getPerpendicularPos(MotionEvent ev) {
- return mSwipeDirection == X ? ev.getY() : ev.getX();
- }
-
- protected float getTranslation(View v) {
- return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
- }
-
- private float getVelocity(VelocityTracker vt) {
- return mSwipeDirection == X ? vt.getXVelocity() :
- vt.getYVelocity();
- }
-
- protected ObjectAnimator createTranslationAnimation(View v, float newPos) {
- ObjectAnimator anim = ObjectAnimator.ofFloat(v,
- mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
- return anim;
- }
-
- private float getPerpendicularVelocity(VelocityTracker vt) {
- return mSwipeDirection == X ? vt.getYVelocity() :
- vt.getXVelocity();
- }
-
- protected Animator getViewTranslationAnimator(View v, float target,
- AnimatorUpdateListener listener) {
- ObjectAnimator anim = createTranslationAnimation(v, target);
- if (listener != null) {
- anim.addUpdateListener(listener);
- }
- return anim;
- }
-
- protected void setTranslation(View v, float translate) {
- if (v == null) {
- return;
- }
- if (mSwipeDirection == X) {
- v.setTranslationX(translate);
- } else {
- v.setTranslationY(translate);
- }
- }
-
- protected float getSize(View v) {
- return mSwipeDirection == X ? v.getMeasuredWidth() :
- v.getMeasuredHeight();
- }
-
- public void setMinSwipeProgress(float minSwipeProgress) {
- mMinSwipeProgress = minSwipeProgress;
- }
-
- public void setMaxSwipeProgress(float maxSwipeProgress) {
- mMaxSwipeProgress = maxSwipeProgress;
- }
-
- private float getSwipeProgressForOffset(View view, float translation) {
- float viewSize = getSize(view);
- float result = Math.abs(translation / viewSize);
- return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress);
- }
-
- private float getSwipeAlpha(float progress) {
- return Math.min(0, Math.max(1, progress / SWIPE_PROGRESS_FADE_END));
- }
-
- private void updateSwipeProgressFromOffset(View animView, boolean dismissable) {
- updateSwipeProgressFromOffset(animView, dismissable, getTranslation(animView));
- }
-
- private void updateSwipeProgressFromOffset(View animView, boolean dismissable,
- float translation) {
- float swipeProgress = getSwipeProgressForOffset(animView, translation);
- if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) {
- if (FADE_OUT_DURING_SWIPE && dismissable) {
- float alpha = swipeProgress;
- if (!mDisableHwLayers) {
- if (alpha != 0f && alpha != 1f) {
- animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- } else {
- animView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
- animView.setAlpha(getSwipeAlpha(swipeProgress));
- }
- }
- invalidateGlobalRegion(animView);
- }
-
- // invalidate the view's own bounds all the way up the view hierarchy
- public static void invalidateGlobalRegion(View view) {
- invalidateGlobalRegion(
- view,
- new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
- }
-
- // invalidate a rectangle relative to the view's coordinate system all the way up the view
- // hierarchy
- public static void invalidateGlobalRegion(View view, RectF childBounds) {
- //childBounds.offset(view.getTranslationX(), view.getTranslationY());
- if (DEBUG_INVALIDATE)
- Log.v(TAG, "-------------");
- while (view.getParent() != null && view.getParent() instanceof View) {
- view = (View) view.getParent();
- view.getMatrix().mapRect(childBounds);
- view.invalidate((int) Math.floor(childBounds.left),
- (int) Math.floor(childBounds.top),
- (int) Math.ceil(childBounds.right),
- (int) Math.ceil(childBounds.bottom));
- if (DEBUG_INVALIDATE) {
- Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
- + "," + (int) Math.floor(childBounds.top)
- + "," + (int) Math.ceil(childBounds.right)
- + "," + (int) Math.ceil(childBounds.bottom));
- }
- }
- }
-
- public void removeLongPressCallback() {
- if (mWatchLongPress != null) {
- mHandler.removeCallbacks(mWatchLongPress);
- mWatchLongPress = null;
- }
- }
-
- public boolean onInterceptTouchEvent(final MotionEvent ev) {
- final int action = ev.getAction();
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mTouchAboveFalsingThreshold = false;
- mDragging = false;
- mSnappingChild = false;
- mLongPressSent = false;
- mVelocityTracker.clear();
- mCurrView = mCallback.getChildAtPosition(ev);
-
- if (mCurrView != null) {
- onDownUpdate(mCurrView);
- mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
- mVelocityTracker.addMovement(ev);
- mInitialTouchPos = getPos(ev);
- mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
- mTranslation = getTranslation(mCurrView);
- if (mLongPressListener != null) {
- if (mWatchLongPress == null) {
- mWatchLongPress = new Runnable() {
- @Override
- public void run() {
- if (mCurrView != null && !mLongPressSent) {
- mLongPressSent = true;
- mCurrView.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
- mCurrView.getLocationOnScreen(mTmpPos);
- final int x = (int) ev.getRawX() - mTmpPos[0];
- final int y = (int) ev.getRawY() - mTmpPos[1];
- mLongPressListener.onLongPress(mCurrView, x, y);
- }
- }
- };
- }
- mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
- }
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (mCurrView != null && !mLongPressSent) {
- mVelocityTracker.addMovement(ev);
- float pos = getPos(ev);
- float perpendicularPos = getPerpendicularPos(ev);
- float delta = pos - mInitialTouchPos;
- float deltaPerpendicular = perpendicularPos - mPerpendicularInitialTouchPos;
- if (Math.abs(delta) > mPagingTouchSlop
- && Math.abs(delta) > Math.abs(deltaPerpendicular)) {
- mCallback.onBeginDrag(mCurrView);
- mDragging = true;
- mInitialTouchPos = getPos(ev);
- mTranslation = getTranslation(mCurrView);
- removeLongPressCallback();
- }
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- final boolean captured = (mDragging || mLongPressSent);
- mDragging = false;
- mCurrView = null;
- mLongPressSent = false;
- removeLongPressCallback();
- if (captured) return true;
- break;
- }
- return mDragging || mLongPressSent;
- }
-
- /**
- * @param view The view to be dismissed
- * @param velocity The desired pixels/second speed at which the view should move
- * @param useAccelerateInterpolator Should an accelerating Interpolator be used
- */
- public void dismissChild(final View view, float velocity, boolean useAccelerateInterpolator) {
- dismissChild(view, velocity, null /* endAction */, 0 /* delay */,
- useAccelerateInterpolator, 0 /* fixedDuration */, false /* isDismissAll */);
- }
-
- /**
- * @param animView The view to be dismissed
- * @param velocity The desired pixels/second speed at which the view should move
- * @param endAction The action to perform at the end
- * @param delay The delay after which we should start
- * @param useAccelerateInterpolator Should an accelerating Interpolator be used
- * @param fixedDuration If not 0, this exact duration will be taken
- */
- public void dismissChild(final View animView, float velocity, final Runnable endAction,
- long delay, boolean useAccelerateInterpolator, long fixedDuration,
- boolean isDismissAll) {
- final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
- float newPos;
- boolean isLayoutRtl = animView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-
- // if we use the Menu to dismiss an item in landscape, animate up
- boolean animateUpForMenu = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll)
- && mSwipeDirection == Y;
- // if the language is rtl we prefer swiping to the left
- boolean animateLeftForRtl = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll)
- && isLayoutRtl;
- boolean animateLeft = velocity < 0
- || (velocity == 0 && getTranslation(animView) < 0 && !isDismissAll);
-
- if (animateLeft || animateLeftForRtl || animateUpForMenu) {
- newPos = -getSize(animView);
- } else {
- newPos = getSize(animView);
- }
- long duration;
- if (fixedDuration == 0) {
- duration = MAX_ESCAPE_ANIMATION_DURATION;
- if (velocity != 0) {
- duration = Math.min(duration,
- (int) (Math.abs(newPos - getTranslation(animView)) * 1000f / Math
- .abs(velocity))
- );
- } else {
- duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
- }
- } else {
- duration = fixedDuration;
- }
-
- if (!mDisableHwLayers) {
- animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
- AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
- public void onAnimationUpdate(ValueAnimator animation) {
- onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed);
- }
- };
-
- Animator anim = getViewTranslationAnimator(animView, newPos, updateListener);
- if (anim == null) {
- return;
- }
- if (useAccelerateInterpolator) {
- anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- anim.setDuration(duration);
- } else {
- mFlingAnimationUtils.applyDismissing(anim, getTranslation(animView),
- newPos, velocity, getSize(animView));
- }
- if (delay > 0) {
- anim.setStartDelay(delay);
- }
- anim.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- public void onAnimationEnd(Animator animation) {
- updateSwipeProgressFromOffset(animView, canBeDismissed);
- mDismissPendingMap.remove(animView);
- if (!mCancelled) {
- mCallback.onChildDismissed(animView);
- }
- if (endAction != null) {
- endAction.run();
- }
- if (!mDisableHwLayers) {
- animView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
- });
-
- prepareDismissAnimation(animView, anim);
- mDismissPendingMap.put(animView, anim);
- anim.start();
- }
-
- /**
- * Called to update the dismiss animation.
- */
- protected void prepareDismissAnimation(View view, Animator anim) {
- // Do nothing
- }
-
- public void snapChild(final View animView, final float targetLeft, float velocity) {
- final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
- AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
- public void onAnimationUpdate(ValueAnimator animation) {
- onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed);
- }
- };
-
- Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener);
- if (anim == null) {
- return;
- }
- int duration = SNAP_ANIM_LEN;
- anim.setDuration(duration);
- anim.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- mSnappingChild = false;
- updateSwipeProgressFromOffset(animView, canBeDismissed);
- mCallback.onChildSnappedBack(animView, targetLeft);
- }
- });
- prepareSnapBackAnimation(animView, anim);
- mSnappingChild = true;
- anim.start();
- }
-
- /**
- * Called to update the snap back animation.
- */
- protected void prepareSnapBackAnimation(View view, Animator anim) {
- // Do nothing
- }
-
- /**
- * Called when there's a down event.
- */
- public void onDownUpdate(View currView) {
- // Do nothing
- }
-
- /**
- * Called on a move event.
- */
- protected void onMoveUpdate(View view, float totalTranslation, float delta) {
- // Do nothing
- }
-
- /**
- * Called in {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)} when the current
- * view is being animated to dismiss or snap.
- */
- public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) {
- updateSwipeProgressFromOffset(animView, canBeDismissed, value);
- }
-
- private void snapChildInstantly(final View view) {
- final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
- setTranslation(view, 0);
- updateSwipeProgressFromOffset(view, canAnimViewBeDismissed);
- }
-
- /**
- * Called when a view is updated to be non-dismissable, if the view was being dismissed before
- * the update this will handle snapping it back into place.
- *
- * @param view the view to snap if necessary.
- * @param animate whether to animate the snap or not.
- * @param targetLeft the target to snap to.
- */
- public void snapChildIfNeeded(final View view, boolean animate, float targetLeft) {
- if ((mDragging && mCurrView == view) || mSnappingChild) {
- return;
- }
- boolean needToSnap = false;
- Animator dismissPendingAnim = mDismissPendingMap.get(view);
- if (dismissPendingAnim != null) {
- needToSnap = true;
- dismissPendingAnim.cancel();
- } else if (getTranslation(view) != 0) {
- needToSnap = true;
- }
- if (needToSnap) {
- if (animate) {
- snapChild(view, targetLeft, 0.0f /* velocity */);
- } else {
- snapChildInstantly(view);
- }
- }
- }
-
- public boolean onTouchEvent(MotionEvent ev) {
- if (mLongPressSent) {
- return true;
- }
-
- if (!mDragging) {
- if (mCallback.getChildAtPosition(ev) != null) {
-
- // We are dragging directly over a card, make sure that we also catch the gesture
- // even if nobody else wants the touch event.
- onInterceptTouchEvent(ev);
- return true;
- } else {
-
- // We are not doing anything, make sure the long press callback
- // is not still ticking like a bomb waiting to go off.
- removeLongPressCallback();
- return false;
- }
- }
-
- mVelocityTracker.addMovement(ev);
- final int action = ev.getAction();
- switch (action) {
- case MotionEvent.ACTION_OUTSIDE:
- case MotionEvent.ACTION_MOVE:
- if (mCurrView != null) {
- float delta = getPos(ev) - mInitialTouchPos;
- float absDelta = Math.abs(delta);
- if (absDelta >= getFalsingThreshold()) {
- mTouchAboveFalsingThreshold = true;
- }
- // don't let items that can't be dismissed be dragged more than
- // maxScrollDistance
- if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {
- float size = getSize(mCurrView);
- float maxScrollDistance = 0.25f * size;
- if (absDelta >= size) {
- delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
- } else {
- delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2));
- }
- }
-
- setTranslation(mCurrView, mTranslation + delta);
- updateSwipeProgressFromOffset(mCurrView, mCanCurrViewBeDimissed);
- onMoveUpdate(mCurrView, mTranslation + delta, delta);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (mCurrView == null) {
- break;
- }
- mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity());
- float velocity = getVelocity(mVelocityTracker);
-
- if (!handleUpEvent(ev, mCurrView, velocity, getTranslation(mCurrView))) {
- if (isDismissGesture(ev)) {
- // flingadingy
- dismissChild(mCurrView, velocity,
- !swipedFastEnough() /* useAccelerateInterpolator */);
- } else {
- // snappity
- mCallback.onDragCancelled(mCurrView);
- snapChild(mCurrView, 0 /* leftTarget */, velocity);
- }
- mCurrView = null;
- }
- mDragging = false;
- break;
- }
- return true;
- }
-
- private int getFalsingThreshold() {
- float factor = mCallback.getFalsingThresholdFactor();
- return (int) (mFalsingThreshold * factor);
- }
-
- private float getMaxVelocity() {
- return MAX_DISMISS_VELOCITY * mDensityScale;
- }
-
- protected float getEscapeVelocity() {
- return getUnscaledEscapeVelocity() * mDensityScale;
- }
-
- protected float getUnscaledEscapeVelocity() {
- return SWIPE_ESCAPE_VELOCITY;
- }
-
- protected long getMaxEscapeAnimDuration() {
- return MAX_ESCAPE_ANIMATION_DURATION;
- }
-
- protected boolean swipedFarEnough() {
- float translation = getTranslation(mCurrView);
- return DISMISS_IF_SWIPED_FAR_ENOUGH && Math.abs(translation) > 0.4 * getSize(mCurrView);
- }
-
- protected boolean isDismissGesture(MotionEvent ev) {
- boolean falsingDetected = mCallback.isAntiFalsingNeeded() && !mTouchAboveFalsingThreshold;
- return !falsingDetected && (swipedFastEnough() || swipedFarEnough())
- && ev.getActionMasked() == MotionEvent.ACTION_UP
- && mCallback.canChildBeDismissed(mCurrView);
- }
-
- protected boolean swipedFastEnough() {
- float velocity = getVelocity(mVelocityTracker);
- float translation = getTranslation(mCurrView);
- boolean ret = (Math.abs(velocity) > getEscapeVelocity())
- && (velocity > 0) == (translation > 0);
- return ret;
- }
-
- protected boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
- float translation) {
- return false;
- }
-
- public interface Callback {
- View getChildAtPosition(MotionEvent ev);
-
- boolean canChildBeDismissed(View v);
-
- boolean isAntiFalsingNeeded();
-
- void onBeginDrag(View v);
-
- void onChildDismissed(View v);
-
- void onDragCancelled(View v);
-
- /**
- * Called when the child is snapped to a position.
- *
- * @param animView the view that was snapped.
- * @param targetLeft the left position the view was snapped to.
- */
- void onChildSnappedBack(View animView, float targetLeft);
-
- /**
- * Updates the swipe progress on a child.
- *
- * @return if true, prevents the default alpha fading.
- */
- boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress);
-
- /**
- * @return The factor the falsing threshold should be multiplied with
- */
- float getFalsingThresholdFactor();
- }
-
- /**
- * Equivalent to View.OnLongClickListener with coordinates
- */
- public interface LongPressListener {
- /**
- * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
- * @return whether the longpress was handled
- */
- boolean onLongPress(View v, int x, int y);
- }
-} \ No newline at end of file
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 4dc3c1c0d..d26f9f646 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -39,12 +39,14 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.config.FeatureFlags;
/**
* A frame layout which contains a QSB. This internally uses fragment to bind the view, which
* allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}.
+ *
+ * Note: AppWidgetManagerCompat can be disabled using FeatureFlags. In QSB, we should use
+ * AppWidgetManager directly, so that it keeps working in that case.
*/
public class QsbContainerView extends FrameLayout {
@@ -106,7 +108,7 @@ public class QsbContainerView extends FrameLayout {
return QsbWidgetHostView.getDefaultView(container);
}
- AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(activity);
+ AppWidgetManager widgetManager = AppWidgetManager.getInstance(activity);
InvariantDeviceProfile idp = LauncherAppState.getIDP(activity);
Bundle opts = new Bundle();
@@ -129,7 +131,8 @@ public class QsbContainerView extends FrameLayout {
}
widgetId = mQsbWidgetHost.allocateAppWidgetId();
- isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(widgetId, mWidgetInfo, opts);
+ isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(
+ widgetId, mWidgetInfo.getProfile(), mWidgetInfo.provider, opts);
if (!isWidgetBound) {
mQsbWidgetHost.deleteAppWidgetId(widgetId);
widgetId = -1;
diff --git a/src/com/android/launcher3/touch/OverScroll.java b/src/com/android/launcher3/touch/OverScroll.java
new file mode 100644
index 000000000..dc801ec4c
--- /dev/null
+++ b/src/com/android/launcher3/touch/OverScroll.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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.touch;
+
+/**
+ * Utility methods for overscroll damping and related effect.
+ */
+public class OverScroll {
+
+ private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
+
+ /**
+ * This curve determines how the effect of scrolling over the limits of the page diminishes
+ * as the user pulls further and further from the bounds
+ *
+ * @param f The percentage of how much the user has overscrolled.
+ * @return A transformed percentage based on the influence curve.
+ */
+ private static float overScrollInfluenceCurve(float f) {
+ f -= 1.0f;
+ return f * f * f + 1.0f;
+ }
+
+ /**
+ * @param amount The original amount overscrolled.
+ * @param max The maximum amount that the View can overscroll.
+ * @return The dampened overscroll amount.
+ */
+ public static int dampedScroll(float amount, int max) {
+ if (Float.compare(amount, 0) == 0) return 0;
+
+ float f = amount / max;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ return Math.round(OVERSCROLL_DAMP_FACTOR * f * max);
+ }
+}
diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index 13c4f63f0..be4648eef 100644
--- a/src/com/android/launcher3/allapps/VerticalPullDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -1,32 +1,52 @@
-package com.android.launcher3.allapps;
+/*
+ * Copyright (C) 2017 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.touch;
+import static android.view.MotionEvent.INVALID_POINTER_ID;
import android.content.Context;
+import android.graphics.PointF;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
/**
- * One dimensional scroll gesture detector for all apps container pull up interaction.
- * Client (e.g., AllAppsTransitionController) of this class can register a listener.
- * <p/>
- * Features that this gesture detector can support.
+ * One dimensional scroll/drag/swipe gesture detector.
+ *
+ * Definition of swipe is different from android system in that this detector handles
+ * 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before
+ * swipe action happens
*/
-public class VerticalPullDetector {
+public class SwipeDetector {
private static final boolean DBG = false;
- private static final String TAG = "VerticalPullDetector";
-
- private final float mTouchSlop;
+ private static final String TAG = "SwipeDetector";
private int mScrollConditions;
- public static final int DIRECTION_UP = 1 << 0;
- public static final int DIRECTION_DOWN = 1 << 1;
- public static final int DIRECTION_BOTH = DIRECTION_DOWN | DIRECTION_UP;
+ public static final int DIRECTION_POSITIVE = 1 << 0;
+ public static final int DIRECTION_NEGATIVE = 1 << 1;
+ public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
private static final float ANIMATION_DURATION = 1200;
private static final float FAST_FLING_PX_MS = 10;
+ protected int mActivePointerId = INVALID_POINTER_ID;
+
/**
* The minimum release velocity in pixels per millisecond that triggers fling..
*/
@@ -47,6 +67,42 @@ public class VerticalPullDetector {
SETTLING // onDragEnd
}
+ public static abstract class Direction {
+
+ abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint);
+
+ /**
+ * Distance in pixels a touch can wander before we think the user is scrolling.
+ */
+ abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
+ }
+
+ public static final Direction VERTICAL = new Direction() {
+
+ @Override
+ float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+ return ev.getY(pointerIndex) - refPoint.y;
+ }
+
+ @Override
+ float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+ return Math.abs(ev.getX(pointerIndex) - downPos.x);
+ }
+ };
+
+ public static final Direction HORIZONTAL = new Direction() {
+
+ @Override
+ float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+ return ev.getX(pointerIndex) - refPoint.x;
+ }
+
+ @Override
+ float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+ return Math.abs(ev.getY(pointerIndex) - downPos.y);
+ }
+ };
+
//------------------- ScrollState transition diagram -----------------------------------
//
// IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
@@ -93,27 +149,24 @@ public class VerticalPullDetector {
return mState == ScrollState.DRAGGING;
}
- private float mDownX;
- private float mDownY;
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private final Direction mDir;
+
+ private final float mTouchSlop;
+
+ /* Client of this gesture detector can register a callback. */
+ private final Listener mListener;
- private float mLastY;
private long mCurrentMillis;
private float mVelocity;
private float mLastDisplacement;
- private float mDisplacementY;
- private float mDisplacementX;
+ private float mDisplacement;
private float mSubtractDisplacement;
private boolean mIgnoreSlopWhenSettling;
- /* Client of this gesture detector can register a callback. */
- private Listener mListener;
-
- public void setListener(Listener l) {
- mListener = l;
- }
-
public interface Listener {
void onDragStart(boolean start);
@@ -122,8 +175,15 @@ public class VerticalPullDetector {
void onDragEnd(float velocity, boolean fling);
}
- public VerticalPullDetector(Context context) {
- mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
+ this(ViewConfiguration.get(context).getScaledTouchSlop(), l, dir);
+ }
+
+ @VisibleForTesting
+ protected SwipeDetector(float touchSlope, @NonNull Listener l, @NonNull Direction dir) {
+ mTouchSlop = touchSlope;
+ mListener = l;
+ mDir = dir;
}
public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
@@ -131,51 +191,65 @@ public class VerticalPullDetector {
mIgnoreSlopWhenSettling = ignoreSlop;
}
- private boolean shouldScrollStart() {
- // reject cases where the slop condition is not met.
- if (Math.abs(mDisplacementY) < mTouchSlop) {
+ private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) {
+ // reject cases where the angle or slop condition is not met.
+ if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop)
+ > Math.abs(mDisplacement)) {
return false;
}
- // reject cases where the angle condition is not met.
- float deltaY = Math.abs(mDisplacementY);
- float deltaX = Math.max(Math.abs(mDisplacementX), 1);
- if (deltaX > deltaY) {
- return false;
- }
// Check if the client is interested in scroll in current direction.
- if (((mScrollConditions & DIRECTION_DOWN) > 0 && mDisplacementY > 0) ||
- ((mScrollConditions & DIRECTION_UP) > 0 && mDisplacementY < 0)) {
+ if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDisplacement > 0) ||
+ ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDisplacement < 0)) {
return true;
}
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
- switch (ev.getAction()) {
+ switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- mDownX = ev.getX();
- mDownY = ev.getY();
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
mLastDisplacement = 0;
- mDisplacementY = 0;
+ mDisplacement = 0;
mVelocity = 0;
if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
setState(ScrollState.DRAGGING);
}
break;
+ //case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP:
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ break;
case MotionEvent.ACTION_MOVE:
- mDisplacementX = ev.getX() - mDownX;
- mDisplacementY = ev.getY() - mDownY;
- computeVelocity(ev);
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos);
+ computeVelocity(mDir.getDisplacement(ev, pointerIndex, mLastPos),
+ ev.getEventTime());
// handle state and listener calls.
- if (mState != ScrollState.DRAGGING && shouldScrollStart()) {
+ if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
setState(ScrollState.DRAGGING);
}
if (mState == ScrollState.DRAGGING) {
reportDragging();
}
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
@@ -185,12 +259,8 @@ public class VerticalPullDetector {
}
break;
default:
- //TODO: add multi finger tracking by tracking active pointer.
break;
}
- // Do house keeping.
- mLastDisplacement = mDisplacementY;
- mLastY = ev.getY();
return true;
}
@@ -210,7 +280,7 @@ public class VerticalPullDetector {
if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
mSubtractDisplacement = 0;
}
- if (mDisplacementY > 0) {
+ if (mDisplacement > 0) {
mSubtractDisplacement = mTouchSlop;
} else {
mSubtractDisplacement = -mTouchSlop;
@@ -218,14 +288,14 @@ public class VerticalPullDetector {
}
private boolean reportDragging() {
- float delta = mDisplacementY - mLastDisplacement;
- if (delta != 0) {
+ if (mDisplacement != mLastDisplacement) {
if (DBG) {
Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f",
- mDisplacementY, mVelocity));
+ mDisplacement, mVelocity));
}
- return mListener.onDrag(mDisplacementY - mSubtractDisplacement, mVelocity);
+ mLastDisplacement = mDisplacement;
+ return mListener.onDrag(mDisplacement - mSubtractDisplacement, mVelocity);
}
return true;
}
@@ -233,19 +303,15 @@ public class VerticalPullDetector {
private void reportDragEnd() {
if (DBG) {
Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
- mDisplacementY, mVelocity));
+ mDisplacement, mVelocity));
}
mListener.onDragEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
}
/**
- * Computes the damped velocity using the two motion events and the previous velocity.
+ * Computes the damped velocity.
*/
- private float computeVelocity(MotionEvent to) {
- return computeVelocity(to.getY() - mLastY, to.getEventTime());
- }
-
public float computeVelocity(float delta, long currentMillis) {
long previousMillis = mCurrentMillis;
mCurrentMillis = currentMillis;
@@ -275,7 +341,7 @@ public class VerticalPullDetector {
return (1.0f - alpha) * from + alpha * to;
}
- public long calculateDuration(float velocity, float progressNeeded) {
+ public static long calculateDuration(float velocity, float progressNeeded) {
// TODO: make these values constants after tuning.
float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
float travelDistance = Math.max(0.2f, progressNeeded);
diff --git a/src/com/android/launcher3/util/SettingsObserver.java b/src/com/android/launcher3/util/SettingsObserver.java
new file mode 100644
index 000000000..6baa24293
--- /dev/null
+++ b/src/com/android/launcher3/util/SettingsObserver.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+
+public interface SettingsObserver {
+
+ /**
+ * Registers the content observer to call {@link #onSettingChanged(boolean)} when any of the
+ * passed settings change. The value passed to onSettingChanged() is based on the key setting.
+ */
+ void register(String keySetting, String ... dependentSettings);
+ void unregister();
+ void onSettingChanged(boolean keySettingEnabled);
+
+
+ abstract class Secure extends ContentObserver implements SettingsObserver {
+ private ContentResolver mResolver;
+ private String mKeySetting;
+
+ public Secure(ContentResolver resolver) {
+ super(new Handler());
+ mResolver = resolver;
+ }
+
+ @Override
+ public void register(String keySetting, String ... dependentSettings) {
+ mKeySetting = keySetting;
+ mResolver.registerContentObserver(
+ Settings.Secure.getUriFor(mKeySetting), false, this);
+ for (String setting : dependentSettings) {
+ mResolver.registerContentObserver(
+ Settings.Secure.getUriFor(setting), false, this);
+ }
+ onChange(true);
+ }
+
+ @Override
+ public void unregister() {
+ mResolver.unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ onSettingChanged(Settings.Secure.getInt(mResolver, mKeySetting, 1) == 1);
+ }
+ }
+
+ abstract class System extends ContentObserver implements SettingsObserver {
+ private ContentResolver mResolver;
+ private String mKeySetting;
+
+ public System(ContentResolver resolver) {
+ super(new Handler());
+ mResolver = resolver;
+ }
+
+ @Override
+ public void register(String keySetting, String ... dependentSettings) {
+ mKeySetting = keySetting;
+ mResolver.registerContentObserver(
+ Settings.System.getUriFor(mKeySetting), false, this);
+ for (String setting : dependentSettings) {
+ mResolver.registerContentObserver(
+ Settings.System.getUriFor(setting), false, this);
+ }
+ onChange(true);
+ }
+
+ @Override
+ public void unregister() {
+ mResolver.unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ onSettingChanged(Settings.System.getInt(mResolver, mKeySetting, 1) == 1);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
index 629f30c26..5387be839 100644
--- a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
+++ b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
@@ -15,10 +15,8 @@
*/
package com.android.launcher3.widget;
-import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
-import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,7 +24,6 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.util.PendingRequestArgs;
/**
@@ -56,15 +53,8 @@ public class WidgetAddFlowHandler implements Parcelable {
public void startBindFlow(Launcher launcher, int appWidgetId, ItemInfo info, int requestCode) {
launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
-
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mProviderInfo.provider);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE,
- mProviderInfo.getProfile());
- // TODO: we need to make sure that this accounts for the options bundle.
- // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
- launcher.startActivityForResult(intent, requestCode);
+ launcher.getAppWidgetHost()
+ .startBindFlow(launcher, appWidgetId, mProviderInfo, requestCode);
}
/**
@@ -85,9 +75,7 @@ public class WidgetAddFlowHandler implements Parcelable {
return false;
}
launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
-
- AppWidgetManagerCompat.getInstance(launcher).startConfigActivity(
- mProviderInfo, appWidgetId, launcher, launcher.getAppWidgetHost(), requestCode);
+ launcher.getAppWidgetHost().startConfigActivity(launcher, appWidgetId, requestCode);
return true;
}
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index b2fb09157..01101ac74 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -40,7 +40,7 @@ import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.VerticalPullDetector;
+import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
@@ -58,7 +58,7 @@ import java.util.List;
* Bottom sheet for the "Widgets" system shortcut in the long-press popup.
*/
public class WidgetsBottomSheet extends AbstractFloatingView implements Insettable, TouchController,
- VerticalPullDetector.Listener, View.OnClickListener, View.OnLongClickListener,
+ SwipeDetector.Listener, View.OnClickListener, View.OnLongClickListener,
DragController.DragListener {
private int mTranslationYOpen;
@@ -69,9 +69,9 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab
private ItemInfo mOriginalItemInfo;
private ObjectAnimator mOpenCloseAnimator;
private Interpolator mFastOutSlowInInterpolator;
- private VerticalPullDetector.ScrollInterpolator mScrollInterpolator;
+ private SwipeDetector.ScrollInterpolator mScrollInterpolator;
private Rect mInsets;
- private VerticalPullDetector mVerticalPullDetector;
+ private SwipeDetector mSwipeDetector;
private GradientView mGradientBackground;
public WidgetsBottomSheet(Context context, AttributeSet attrs) {
@@ -85,11 +85,11 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab
mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);
mFastOutSlowInInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
- mScrollInterpolator = new VerticalPullDetector.ScrollInterpolator();
+ mScrollInterpolator = new SwipeDetector.ScrollInterpolator();
mInsets = new Rect();
- mVerticalPullDetector = new VerticalPullDetector(context);
- mVerticalPullDetector.setListener(this);
- mGradientBackground = (GradientView) mLauncher.findViewById(R.id.gradient_bg);
+ mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL);
+ mGradientBackground = (GradientView) mLauncher.getLayoutInflater().inflate(
+ R.layout.gradient_bg, mLauncher.getDragLayer(), false);
}
@Override
@@ -107,6 +107,8 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab
onWidgetsBound();
+ mLauncher.getDragLayer().addView(mGradientBackground);
+ mGradientBackground.setVisibility(VISIBLE);
mLauncher.getDragLayer().addView(this);
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
setTranslationY(mTranslationYClosed);
@@ -192,7 +194,7 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mVerticalPullDetector.finishedScrolling();
+ mSwipeDetector.finishedScrolling();
}
});
mOpenCloseAnimator.setInterpolator(mFastOutSlowInInterpolator);
@@ -213,24 +215,27 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mIsOpen = false;
- mVerticalPullDetector.finishedScrolling();
- ((ViewGroup) getParent()).removeView(WidgetsBottomSheet.this);
- mLauncher.getSystemUiController().updateUiState(
- SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
+ mSwipeDetector.finishedScrolling();
+ onCloseComplete();
}
});
- mOpenCloseAnimator.setInterpolator(mVerticalPullDetector.isIdleState()
+ mOpenCloseAnimator.setInterpolator(mSwipeDetector.isIdleState()
? mFastOutSlowInInterpolator : mScrollInterpolator);
mOpenCloseAnimator.start();
} else {
setTranslationY(mTranslationYClosed);
- mLauncher.getSystemUiController().updateUiState(
- SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
- mIsOpen = false;
+ onCloseComplete();
}
}
+ private void onCloseComplete() {
+ mIsOpen = false;
+ mLauncher.getDragLayer().removeView(mGradientBackground);
+ mLauncher.getDragLayer().removeView(WidgetsBottomSheet.this);
+ mLauncher.getSystemUiController().updateUiState(
+ SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
+ }
+
@Override
protected boolean isOfType(@FloatingViewType int type) {
return (type & TYPE_WIDGETS_BOTTOM_SHEET) != 0;
@@ -259,7 +264,7 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab
getPaddingRight() + rightInset, getPaddingBottom() + bottomInset);
}
- /* VerticalPullDetector.Listener */
+ /* SwipeDetector.Listener */
@Override
public void onDragStart(boolean start) {
@@ -285,12 +290,12 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab
public void onDragEnd(float velocity, boolean fling) {
if ((fling && velocity > 0) || getTranslationY() > (mTranslationYRange) / 2) {
mScrollInterpolator.setVelocityAtZero(velocity);
- mOpenCloseAnimator.setDuration(mVerticalPullDetector.calculateDuration(velocity,
+ mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity,
(mTranslationYClosed - getTranslationY()) / mTranslationYRange));
close(true);
} else {
mIsOpen = false;
- mOpenCloseAnimator.setDuration(mVerticalPullDetector.calculateDuration(velocity,
+ mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity,
(getTranslationY() - mTranslationYOpen) / mTranslationYRange));
open(true);
}
@@ -298,17 +303,17 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
- return mVerticalPullDetector.onTouchEvent(ev);
+ return mSwipeDetector.onTouchEvent(ev);
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- int directionsToDetectScroll = mVerticalPullDetector.isIdleState() ?
- VerticalPullDetector.DIRECTION_DOWN : 0;
- mVerticalPullDetector.setDetectableScrollConditions(
+ int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
+ SwipeDetector.DIRECTION_NEGATIVE : 0;
+ mSwipeDetector.setDetectableScrollConditions(
directionsToDetectScroll, false);
- mVerticalPullDetector.onTouchEvent(ev);
- return mVerticalPullDetector.isDraggingOrSettling();
+ mSwipeDetector.onTouchEvent(ev);
+ return mSwipeDetector.isDraggingOrSettling();
}
/* DragListener */