diff options
Diffstat (limited to 'src/com/android/launcher3')
49 files changed, 470 insertions, 931 deletions
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index daf20323d..322383778 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -34,6 +34,7 @@ import android.widget.LinearLayout; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.BaseDragLayer; import java.lang.annotation.Retention; @@ -84,7 +85,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch // Usually we show the back button when a floating view is open. Instead, hide for these types. public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE; - public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE; + public static final int TYPE_ACCESSIBLE = TYPE_ALL + & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_QUICKSTEP_PREVIEW; protected boolean mIsOpen; @@ -151,7 +153,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch if (mIsOpen) { sendAccessibilityEvent(TYPE_VIEW_FOCUSED); } - BaseDraggingActivity.fromContext(getContext()).getDragLayer() + ActivityContext.lookupContext(getContext()).getDragLayer() .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED); } @@ -160,7 +162,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch } protected static <T extends AbstractFloatingView> T getOpenView( - BaseDraggingActivity activity, @FloatingViewType int type) { + ActivityContext activity, @FloatingViewType int type) { BaseDragLayer dragLayer = activity.getDragLayer(); // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, // and will be one of the last views. @@ -176,7 +178,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch return null; } - public static void closeOpenContainer(BaseDraggingActivity activity, + public static void closeOpenContainer(ActivityContext activity, @FloatingViewType int type) { AbstractFloatingView view = getOpenView(activity, type); if (view != null) { @@ -184,7 +186,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch } } - public static void closeOpenViews(BaseDraggingActivity activity, boolean animate, + public static void closeOpenViews(ActivityContext activity, boolean animate, @FloatingViewType int type) { BaseDragLayer dragLayer = activity.getDragLayer(); // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, @@ -200,20 +202,20 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch } } - public static void closeAllOpenViews(BaseDraggingActivity activity, boolean animate) { + public static void closeAllOpenViews(ActivityContext activity, boolean animate) { closeOpenViews(activity, animate, TYPE_ALL); activity.finishAutoCancelActionMode(); } - public static void closeAllOpenViews(BaseDraggingActivity activity) { + public static void closeAllOpenViews(ActivityContext activity) { closeAllOpenViews(activity, true); } - public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) { + public static AbstractFloatingView getTopOpenView(ActivityContext activity) { return getTopOpenViewWithType(activity, TYPE_ALL); } - public static AbstractFloatingView getTopOpenViewWithType(BaseDraggingActivity activity, + public static AbstractFloatingView getTopOpenViewWithType(ActivityContext activity, @FloatingViewType int type) { return getOpenView(activity, type); } diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java index 923835a67..9b6166ffc 100644 --- a/src/com/android/launcher3/AppFilter.java +++ b/src/com/android/launcher3/AppFilter.java @@ -3,10 +3,12 @@ package com.android.launcher3; import android.content.ComponentName; import android.content.Context; -public class AppFilter { +import com.android.launcher3.util.ResourceBasedOverride; + +public class AppFilter implements ResourceBasedOverride { public static AppFilter newInstance(Context context) { - return Utilities.getOverrideObject(AppFilter.class, context, R.string.app_filter_class); + return Overrides.getObject(AppFilter.class, context, R.string.app_filter_class); } public boolean shouldShowApp(ComponentName app) { diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java index a4b6f5b05..6c82e6385 100644 --- a/src/com/android/launcher3/BaseActivity.java +++ b/src/com/android/launcher3/BaseActivity.java @@ -26,6 +26,7 @@ import android.content.ContextWrapper; import android.content.Intent; import android.content.res.Configuration; import android.support.annotation.IntDef; +import android.view.ContextThemeWrapper; import android.view.View.AccessibilityDelegate; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; @@ -107,7 +108,7 @@ public abstract class BaseActivity extends Activity implements UserEventDelegate public final UserEventDispatcher getUserEventDispatcher() { if (mUserEventDispatcher == null) { - mUserEventDispatcher = UserEventDispatcher.newInstance(this, mDeviceProfile, this); + mUserEventDispatcher = UserEventDispatcher.newInstance(this, this); } return mUserEventDispatcher; } @@ -116,13 +117,6 @@ public abstract class BaseActivity extends Activity implements UserEventDelegate return Utilities.ATLEAST_NOUGAT && isInMultiWindowMode(); } - public static BaseActivity fromContext(Context context) { - if (context instanceof BaseActivity) { - return (BaseActivity) context; - } - return ((BaseActivity) ((ContextWrapper) context).getBaseContext()); - } - public SystemUiController getSystemUiController() { if (mSystemUiController == null) { mSystemUiController = new SystemUiController(getWindow()); @@ -259,4 +253,14 @@ public abstract class BaseActivity extends Activity implements UserEventDelegate writer.println(" mActivityFlags: " + mActivityFlags); writer.println(" mForceInvisible: " + mForceInvisible); } + + public static <T extends BaseActivity> T fromContext(Context context) { + if (context instanceof BaseActivity) { + return (T) context; + } else if (context instanceof ContextThemeWrapper) { + return fromContext(((ContextWrapper) context).getBaseContext()); + } else { + throw new IllegalArgumentException("Cannot find BaseActivity in parent tree"); + } + } } diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java index eec196e1e..d6635dc13 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -18,8 +18,6 @@ package com.android.launcher3; import android.app.ActivityOptions; import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; import android.graphics.Rect; import android.os.Bundle; @@ -28,23 +26,23 @@ import android.os.StrictMode; import android.os.UserHandle; import android.util.Log; import android.view.ActionMode; -import android.view.Surface; import android.view.View; import android.widget.Toast; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.uioverrides.DisplayRotationListener; import com.android.launcher3.uioverrides.WallpaperColorInfo; -import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.views.BaseDragLayer; +import com.android.launcher3.views.ActivityContext; /** * Extension of BaseActivity allowing support for drag-n-drop */ public abstract class BaseDraggingActivity extends BaseActivity - implements WallpaperColorInfo.OnChangeListener { + implements WallpaperColorInfo.OnChangeListener, ActivityContext { private static final String TAG = "BaseDraggingActivity"; @@ -110,6 +108,7 @@ public abstract class BaseDraggingActivity extends BaseActivity mCurrentActionMode = null; } + @Override public boolean finishAutoCancelActionMode() { if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) { mCurrentActionMode.finish(); @@ -128,13 +127,6 @@ public abstract class BaseDraggingActivity extends BaseActivity public abstract void invalidateParent(ItemInfo info); - public static BaseDraggingActivity fromContext(Context context) { - if (context instanceof BaseDraggingActivity) { - return (BaseDraggingActivity) context; - } - return ((BaseDraggingActivity) ((ContextWrapper) context).getBaseContext()); - } - public Rect getViewBounds(View v) { int[] pos = new int[2]; v.getLocationOnScreen(pos); diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java index 9217ca987..7efb6ec94 100644 --- a/src/com/android/launcher3/FastBitmapDrawable.java +++ b/src/com/android/launcher3/FastBitmapDrawable.java @@ -109,7 +109,7 @@ public class FastBitmapDrawable extends Drawable { @Override public final void draw(Canvas canvas) { - if (mScaleAnimation != null) { + if (mScale != 1f) { int count = canvas.save(); Rect bounds = getBounds(); canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY()); @@ -150,10 +150,23 @@ public class FastBitmapDrawable extends Drawable { return mAlpha; } + public void setScale(float scale) { + if (mScaleAnimation != null) { + mScaleAnimation.cancel(); + mScaleAnimation = null; + } + mScale = scale; + invalidateSelf(); + } + public float getAnimatedScale() { return mScaleAnimation == null ? 1 : mScale; } + public float getScale() { + return mScale; + } + @Override public int getIntrinsicWidth() { return mBitmap.getWidth(); diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index c5ca183f7..f496600c7 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -478,7 +478,8 @@ public class IconCache { info.contentDescription = ""; info.usingLowResIcon = false; } else { - getTitleAndIcon(info, new ActivityInfoProvider(info.getIntent(), info.user), + Intent intent = info.getIntent(); + getTitleAndIcon(info, () -> mLauncherApps.resolveActivity(intent, info.user), true, useLowResIcon); } } @@ -869,22 +870,6 @@ public class IconCache { } } - private class ActivityInfoProvider extends Provider<LauncherActivityInfo> { - - private final Intent mIntent; - private final UserHandle mUser; - - public ActivityInfoProvider(Intent intent, UserHandle user) { - mIntent = intent; - mUser = user; - } - - @Override - public LauncherActivityInfo get() { - return mLauncherApps.resolveActivity(mIntent, mUser); - } - } - /** * Interface for receiving itemInfo with high-res icon. */ diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java index b469a8f45..ed8d03c47 100644 --- a/src/com/android/launcher3/IconProvider.java +++ b/src/com/android/launcher3/IconProvider.java @@ -5,14 +5,16 @@ import android.content.pm.LauncherActivityInfo; import android.graphics.drawable.Drawable; import android.os.Build; +import com.android.launcher3.util.ResourceBasedOverride; + import java.util.Locale; -public class IconProvider { +public class IconProvider implements ResourceBasedOverride { protected String mSystemState; public static IconProvider newInstance(Context context) { - IconProvider provider = Utilities.getOverrideObject( + IconProvider provider = Overrides.getObject( IconProvider.class, context, R.string.icon_provider_class); provider.updateSystemStateString(context); return provider; diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index fe8a841be..b9d45fb02 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -47,7 +47,6 @@ import com.android.launcher3.shortcuts.ShortcutInfoCompat; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Preconditions; -import com.android.launcher3.util.Provider; import com.android.launcher3.util.Thunk; import org.json.JSONException; @@ -486,13 +485,10 @@ public class InstallShortcutReceiver extends BroadcastReceiver { if (Looper.myLooper() == LauncherModel.getWorkerLooper()) { app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */); } else { - app.getModel().updateAndBindShortcutInfo(new Provider<ShortcutInfo>() { - @Override - public ShortcutInfo get() { - app.getIconCache().getTitleAndIcon( - si, activityInfo, false /* useLowResIcon */); - return si; - } + app.getModel().updateAndBindShortcutInfo(() -> { + app.getIconCache().getTitleAndIcon( + si, activityInfo, false /* useLowResIcon */); + return si; }); } return Pair.create((ItemInfo) si, (Object) activityInfo); diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index f63cce58d..22bc162b6 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -29,6 +29,8 @@ import android.view.Display; import android.view.WindowManager; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.util.ConfigMonitor; +import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Thunk; import org.xmlpull.v1.XmlPullParser; @@ -41,8 +43,12 @@ import java.util.Comparator; public class InvariantDeviceProfile { - // This is a static that we use for the default icon size on a 4/5-inch phone - private static float DEFAULT_ICON_SIZE_DP = 60; + // We do not need any synchronization for this variable as its only written on UI thread. + public static final MainThreadInitializedObject<InvariantDeviceProfile> INSTANCE = + new MainThreadInitializedObject<>((c) -> { + new ConfigMonitor(c).register(); + return new InvariantDeviceProfile(c); + }); private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48; @@ -118,7 +124,7 @@ public class InvariantDeviceProfile { } @TargetApi(23) - public InvariantDeviceProfile(Context context) { + private InvariantDeviceProfile(Context context) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); DisplayMetrics dm = new DisplayMetrics(); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 4fe35033b..44d3d5373 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import static android.content.pm.ActivityInfo.CONFIG_LOCALE; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; @@ -74,7 +75,6 @@ import android.view.animation.OvershootInterpolator; import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsTransitionController; @@ -86,6 +86,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragView; +import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.FolderIconPreviewVerifier; import com.android.launcher3.keyboard.CustomActionsPopup; @@ -356,6 +357,11 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override public void onConfigurationChanged(Configuration newConfig) { int diff = newConfig.diff(mOldConfig); + + if ((diff & CONFIG_LOCALE) != 0) { + Folder.setLocaleDependentFields(getResources(), true /* force */); + } + if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) { mUserEventDispatcher = null; initDeviceProfile(mDeviceProfile.inv); @@ -1058,12 +1064,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } 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; - } - }); + return (FolderIcon) mWorkspace.getHomescreenIconByItemId(folderIconId); } /** @@ -2417,10 +2418,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } public static Launcher getLauncher(Context context) { - if (context instanceof Launcher) { - return (Launcher) context; - } - return ((Launcher) ((ContextWrapper) context).getBaseContext()); + return (Launcher) fromContext(context); } /** diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index a46692b0b..5159de17a 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -16,12 +16,13 @@ package com.android.launcher3; +import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING; + import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.Looper; import android.util.Log; import com.android.launcher3.compat.LauncherAppsCompat; @@ -29,21 +30,17 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.notification.NotificationListener; -import com.android.launcher3.util.ConfigMonitor; +import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SettingsObserver; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; - -import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING; - public class LauncherAppState { public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher"; // We do not need any synchronization for this variable as its only written on UI thread. - private static LauncherAppState INSTANCE; + private static final MainThreadInitializedObject<LauncherAppState> INSTANCE = + new MainThreadInitializedObject<>((c) -> new LauncherAppState(c)); private final Context mContext; private final LauncherModel mModel; @@ -53,27 +50,11 @@ public class LauncherAppState { private final SettingsObserver mNotificationBadgingObserver; public static LauncherAppState getInstance(final Context context) { - if (INSTANCE == null) { - if (Looper.myLooper() == Looper.getMainLooper()) { - INSTANCE = new LauncherAppState(context.getApplicationContext()); - } else { - try { - return new MainThreadExecutor().submit(new Callable<LauncherAppState>() { - @Override - public LauncherAppState call() throws Exception { - return LauncherAppState.getInstance(context); - } - }).get(); - } catch (InterruptedException|ExecutionException e) { - throw new RuntimeException(e); - } - } - } - return INSTANCE; + return INSTANCE.get(context); } public static LauncherAppState getInstanceNoCreate() { - return INSTANCE; + return INSTANCE.getNoCreate(); } public Context getContext() { @@ -89,7 +70,7 @@ public class LauncherAppState { Preconditions.assertUIThread(); mContext = context; - mInvariantDeviceProfile = new InvariantDeviceProfile(mContext); + mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(mContext); mIconCache = new IconCache(mContext, mInvariantDeviceProfile); mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache); mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext)); @@ -112,7 +93,6 @@ public class LauncherAppState { mContext.registerReceiver(mModel, filter); UserManagerCompat.getInstance(mContext).enableAndResetCache(); - new ConfigMonitor(mContext).register(); if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) { mNotificationBadgingObserver = null; @@ -171,7 +151,7 @@ public class LauncherAppState { * Shorthand for {@link #getInvariantDeviceProfile()} */ public static InvariantDeviceProfile getIDP(Context context) { - return LauncherAppState.getInstance(context).getInvariantDeviceProfile(); + return InvariantDeviceProfile.INSTANCE.get(context); } private static LauncherProvider getLocalProvider(Context context) { diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java index 4037a23ff..970e55878 100644 --- a/src/com/android/launcher3/LauncherAppTransitionManager.java +++ b/src/com/android/launcher3/LauncherAppTransitionManager.java @@ -23,13 +23,15 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.View; +import com.android.launcher3.util.ResourceBasedOverride; + /** * Manages the opening and closing app transitions from Launcher. */ -public class LauncherAppTransitionManager { +public class LauncherAppTransitionManager implements ResourceBasedOverride { public static LauncherAppTransitionManager newInstance(Context context) { - return Utilities.getOverrideObject(LauncherAppTransitionManager.class, + return Overrides.getObject(LauncherAppTransitionManager.class, context, R.string.app_transition_manager_class); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 37538ae0d..19aa795fe 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -620,15 +620,12 @@ public class LauncherModel extends BroadcastReceiver } public void updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info) { - updateAndBindShortcutInfo(new Provider<ShortcutInfo>() { - @Override - public ShortcutInfo get() { - si.updateFromDeepShortcutInfo(info, mApp.getContext()); - LauncherIcons li = LauncherIcons.obtain(mApp.getContext()); - li.createShortcutIcon(info).applyTo(si); - li.recycle(); - return si; - } + updateAndBindShortcutInfo(() -> { + si.updateFromDeepShortcutInfo(info, mApp.getContext()); + LauncherIcons li = LauncherIcons.obtain(mApp.getContext()); + li.createShortcutIcon(info).applyTo(si); + li.recycle(); + return si; }); } diff --git a/src/com/android/launcher3/LauncherScroller.java b/src/com/android/launcher3/LauncherScroller.java index a9b49556b..e5042c4e2 100644 --- a/src/com/android/launcher3/LauncherScroller.java +++ b/src/com/android/launcher3/LauncherScroller.java @@ -18,541 +18,37 @@ package com.android.launcher3; import android.animation.TimeInterpolator; import android.content.Context; -import android.hardware.SensorManager; -import android.os.Build; -import android.view.ViewConfiguration; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.widget.Scroller; /** - * This class differs from the framework {@link android.widget.Scroller} in that - * you can modify the Interpolator post-construction. + * Extension of {@link android.widget.Scroller} with the ability to modify the + * Interpolator post-construction. */ -public class LauncherScroller { - private int mMode; +public class LauncherScroller extends Scroller { - private int mStartX; - private int mStartY; - private int mFinalX; - private int mFinalY; + private final InterpolatorWrapper mInterpolatorWrapper; - private int mMinX; - private int mMaxX; - private int mMinY; - private int mMaxY; - - private int mCurrX; - private int mCurrY; - private long mStartTime; - private int mDuration; - private float mDurationReciprocal; - private float mDeltaX; - private float mDeltaY; - private boolean mFinished; - private TimeInterpolator mInterpolator; - private boolean mFlywheel; - - private float mVelocity; - private float mCurrVelocity; - private int mDistance; - - private float mFlingFriction = ViewConfiguration.getScrollFriction(); - - private static final int DEFAULT_DURATION = 250; - private static final int SCROLL_MODE = 0; - private static final int FLING_MODE = 1; - - private static float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9)); - private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1) - private static final float START_TENSION = 0.5f; - private static final float END_TENSION = 1.0f; - private static final float P1 = START_TENSION * INFLEXION; - private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION); - - private static final int NB_SAMPLES = 100; - private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1]; - private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1]; - - private float mDeceleration; - private final float mPpi; - - // A context-specific coefficient adjusted to physical values. - private float mPhysicalCoeff; - - static { - float x_min = 0.0f; - float y_min = 0.0f; - for (int i = 0; i < NB_SAMPLES; i++) { - final float alpha = (float) i / NB_SAMPLES; - - float x_max = 1.0f; - float x, tx, coef; - while (true) { - x = x_min + (x_max - x_min) / 2.0f; - coef = 3.0f * x * (1.0f - x); - tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x; - if (Math.abs(tx - alpha) < 1E-5) break; - if (tx > alpha) x_max = x; - else x_min = x; - } - SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x; - - float y_max = 1.0f; - float y, dy; - while (true) { - y = y_min + (y_max - y_min) / 2.0f; - coef = 3.0f * y * (1.0f - y); - dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y; - if (Math.abs(dy - alpha) < 1E-5) break; - if (dy > alpha) y_max = y; - else y_min = y; - } - SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y; - } - SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f; - - // This controls the viscous fluid effect (how much of it) - sViscousFluidScale = 8.0f; - // must be set to 1.0 (used in viscousFluid()) - sViscousFluidNormalize = 1.0f; - sViscousFluidNormalize = 1.0f / viscousFluid(1.0f); - - } - - private static float sViscousFluidScale; - private static float sViscousFluidNormalize; - - public void setInterpolator(TimeInterpolator interpolator) { - mInterpolator = interpolator; - } - - /** - * Create a Scroller with the default duration and interpolator. - */ public LauncherScroller(Context context) { - this(context, null); - } - - /** - * Create a Scroller with the specified interpolator. If the interpolator is - * null, the default (viscous) interpolator will be used. "Flywheel" behavior will - * be in effect for apps targeting Honeycomb or newer. - */ - public LauncherScroller(Context context, Interpolator interpolator) { - this(context, interpolator, - context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB); - } - - /** - * Create a Scroller with the specified interpolator. If the interpolator is - * null, the default (viscous) interpolator will be used. Specify whether or - * not to support progressive "flywheel" behavior in flinging. - */ - public LauncherScroller(Context context, Interpolator interpolator, boolean flywheel) { - mFinished = true; - mInterpolator = interpolator; - mPpi = context.getResources().getDisplayMetrics().density * 160.0f; - mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction()); - mFlywheel = flywheel; - - mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning - } - - /** - * The amount of friction applied to flings. The default value - * is {@link ViewConfiguration#getScrollFriction}. - * - * @param friction A scalar dimension-less value representing the coefficient of - * friction. - */ - public final void setFriction(float friction) { - mDeceleration = computeDeceleration(friction); - mFlingFriction = friction; - } - - private float computeDeceleration(float friction) { - return SensorManager.GRAVITY_EARTH // g (m/s^2) - * 39.37f // inch/meter - * mPpi // pixels per inch - * friction; - } - - /** - * - * Returns whether the scroller has finished scrolling. - * - * @return True if the scroller has finished scrolling, false otherwise. - */ - public final boolean isFinished() { - return mFinished; - } - - /** - * Force the finished field to a particular value. - * - * @param finished The new finished value. - */ - public final void forceFinished(boolean finished) { - mFinished = finished; - } - - /** - * Returns how long the scroll event will take, in milliseconds. - * - * @return The duration of the scroll in milliseconds. - */ - public final int getDuration() { - return mDuration; - } - - /** - * Returns the current X offset in the scroll. - * - * @return The new X offset as an absolute distance from the origin. - */ - public final int getCurrX() { - return mCurrX; - } - - /** - * Returns the current Y offset in the scroll. - * - * @return The new Y offset as an absolute distance from the origin. - */ - public final int getCurrY() { - return mCurrY; - } - - /** - * Returns the current velocity. - * - * @return The original velocity less the deceleration. Result may be - * negative. - */ - public float getCurrVelocity() { - return mMode == FLING_MODE ? - mCurrVelocity : mVelocity - mDeceleration * timePassed() / 2000.0f; - } - - /** - * Returns the start X offset in the scroll. - * - * @return The start X offset as an absolute distance from the origin. - */ - public final int getStartX() { - return mStartX; - } - - /** - * Returns the start Y offset in the scroll. - * - * @return The start Y offset as an absolute distance from the origin. - */ - public final int getStartY() { - return mStartY; - } - - /** - * Returns where the scroll will end. Valid only for "fling" scrolls. - * - * @return The final X offset as an absolute distance from the origin. - */ - public final int getFinalX() { - return mFinalX; + this(context, new InterpolatorWrapper()); } - /** - * Returns where the scroll will end. Valid only for "fling" scrolls. - * - * @return The final Y offset as an absolute distance from the origin. - */ - public final int getFinalY() { - return mFinalY; + private LauncherScroller(Context context, InterpolatorWrapper interpolatorWrapper) { + super(context, interpolatorWrapper); + mInterpolatorWrapper = interpolatorWrapper; } - /** - * Call this when you want to know the new location. If it returns true, - * the animation is not yet finished. - */ - public boolean computeScrollOffset() { - if (mFinished) { - return false; - } - - int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); - - if (timePassed < mDuration) { - switch (mMode) { - case SCROLL_MODE: - float x = timePassed * mDurationReciprocal; - - if (mInterpolator == null) - x = viscousFluid(x); - else - x = mInterpolator.getInterpolation(x); - - mCurrX = mStartX + Math.round(x * mDeltaX); - mCurrY = mStartY + Math.round(x * mDeltaY); - break; - case FLING_MODE: - final float t = (float) timePassed / mDuration; - final int index = (int) (NB_SAMPLES * t); - float distanceCoef = 1.f; - float velocityCoef = 0.f; - if (index < NB_SAMPLES) { - final float t_inf = (float) index / NB_SAMPLES; - final float t_sup = (float) (index + 1) / NB_SAMPLES; - final float d_inf = SPLINE_POSITION[index]; - final float d_sup = SPLINE_POSITION[index + 1]; - velocityCoef = (d_sup - d_inf) / (t_sup - t_inf); - distanceCoef = d_inf + (t - t_inf) * velocityCoef; - } - - mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f; - - mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX)); - // Pin to mMinX <= mCurrX <= mMaxX - mCurrX = Math.min(mCurrX, mMaxX); - mCurrX = Math.max(mCurrX, mMinX); - - mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY)); - // Pin to mMinY <= mCurrY <= mMaxY - mCurrY = Math.min(mCurrY, mMaxY); - mCurrY = Math.max(mCurrY, mMinY); - - if (mCurrX == mFinalX && mCurrY == mFinalY) { - mFinished = true; - } - - break; - } - } - else { - mCurrX = mFinalX; - mCurrY = mFinalY; - mFinished = true; - } - return true; - } - - /** - * Start scrolling by providing a starting point and the distance to travel. - * The scroll will use the default value of 250 milliseconds for the - * duration. - * - * @param startX Starting horizontal scroll offset in pixels. Positive - * numbers will scroll the content to the left. - * @param startY Starting vertical scroll offset in pixels. Positive numbers - * will scroll the content up. - * @param dx Horizontal distance to travel. Positive numbers will scroll the - * content to the left. - * @param dy Vertical distance to travel. Positive numbers will scroll the - * content up. - */ - public void startScroll(int startX, int startY, int dx, int dy) { - startScroll(startX, startY, dx, dy, DEFAULT_DURATION); - } - - /** - * Start scrolling by providing a starting point, the distance to travel, - * and the duration of the scroll. - * - * @param startX Starting horizontal scroll offset in pixels. Positive - * numbers will scroll the content to the left. - * @param startY Starting vertical scroll offset in pixels. Positive numbers - * will scroll the content up. - * @param dx Horizontal distance to travel. Positive numbers will scroll the - * content to the left. - * @param dy Vertical distance to travel. Positive numbers will scroll the - * content up. - * @param duration Duration of the scroll in milliseconds. - */ - public void startScroll(int startX, int startY, int dx, int dy, int duration) { - mMode = SCROLL_MODE; - mFinished = false; - mDuration = duration; - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mStartX = startX; - mStartY = startY; - mFinalX = startX + dx; - mFinalY = startY + dy; - mDeltaX = dx; - mDeltaY = dy; - mDurationReciprocal = 1.0f / (float) mDuration; - } - - /** - * Start scrolling based on a fling gesture. The distance travelled will - * depend on the initial velocity of the fling. - * - * @param startX Starting point of the scroll (X) - * @param startY Starting point of the scroll (Y) - * @param velocityX Initial velocity of the fling (X) measured in pixels per - * second. - * @param velocityY Initial velocity of the fling (Y) measured in pixels per - * second - * @param minX Minimum X value. The scroller will not scroll past this - * point. - * @param maxX Maximum X value. The scroller will not scroll past this - * point. - * @param minY Minimum Y value. The scroller will not scroll past this - * point. - * @param maxY Maximum Y value. The scroller will not scroll past this - * point. - */ - public void fling(int startX, int startY, int velocityX, int velocityY, - int minX, int maxX, int minY, int maxY) { - // Continue a scroll or fling in progress - if (mFlywheel && !mFinished) { - float oldVel = getCurrVelocity(); - - float dx = (float) (mFinalX - mStartX); - float dy = (float) (mFinalY - mStartY); - float hyp = (float) Math.hypot(dx, dy); - - float ndx = dx / hyp; - float ndy = dy / hyp; - - float oldVelocityX = ndx * oldVel; - float oldVelocityY = ndy * oldVel; - if (Math.signum(velocityX) == Math.signum(oldVelocityX) && - Math.signum(velocityY) == Math.signum(oldVelocityY)) { - velocityX += oldVelocityX; - velocityY += oldVelocityY; - } - } - - mMode = FLING_MODE; - mFinished = false; - - float velocity = (float) Math.hypot(velocityX, velocityY); - - mVelocity = velocity; - mDuration = getSplineFlingDuration(velocity); - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mStartX = startX; - mStartY = startY; - - float coeffX = velocity == 0 ? 1.0f : velocityX / velocity; - float coeffY = velocity == 0 ? 1.0f : velocityY / velocity; - - double totalDistance = getSplineFlingDistance(velocity); - mDistance = (int) (totalDistance * Math.signum(velocity)); - - mMinX = minX; - mMaxX = maxX; - mMinY = minY; - mMaxY = maxY; - - mFinalX = startX + (int) Math.round(totalDistance * coeffX); - // Pin to mMinX <= mFinalX <= mMaxX - mFinalX = Math.min(mFinalX, mMaxX); - mFinalX = Math.max(mFinalX, mMinX); - - mFinalY = startY + (int) Math.round(totalDistance * coeffY); - // Pin to mMinY <= mFinalY <= mMaxY - mFinalY = Math.min(mFinalY, mMaxY); - mFinalY = Math.max(mFinalY, mMinY); - } - - private double getSplineDeceleration(float velocity) { - return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff)); + public void setInterpolator(TimeInterpolator interpolator) { + mInterpolatorWrapper.interpolator = interpolator; } - private int getSplineFlingDuration(float velocity) { - final double l = getSplineDeceleration(velocity); - final double decelMinusOne = DECELERATION_RATE - 1.0; - return (int) (1000.0 * Math.exp(l / decelMinusOne)); - } + private static class InterpolatorWrapper implements Interpolator { - private double getSplineFlingDistance(float velocity) { - final double l = getSplineDeceleration(velocity); - final double decelMinusOne = DECELERATION_RATE - 1.0; - return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l); - } + public TimeInterpolator interpolator; - static float viscousFluid(float x) - { - x *= sViscousFluidScale; - if (x < 1.0f) { - x -= (1.0f - (float)Math.exp(-x)); - } else { - float start = 0.36787944117f; // 1/e == exp(-1) - x = 1.0f - (float)Math.exp(1.0f - x); - x = start + x * (1.0f - start); + @Override + public float getInterpolation(float v) { + return interpolator == null ? v : interpolator.getInterpolation(v); } - x *= sViscousFluidNormalize; - return x; - } - - /** - * Stops the animation. Contrary to {@link #forceFinished(boolean)}, - * aborting the animating cause the scroller to move to the final x and y - * position - * - * @see #forceFinished(boolean) - */ - public void abortAnimation() { - mCurrX = mFinalX; - mCurrY = mFinalY; - mFinished = true; - } - - /** - * Extend the scroll animation. This allows a running animation to scroll - * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}. - * - * @param extend Additional time to scroll in milliseconds. - * @see #setFinalX(int) - * @see #setFinalY(int) - */ - public void extendDuration(int extend) { - int passed = timePassed(); - mDuration = passed + extend; - mDurationReciprocal = 1.0f / mDuration; - mFinished = false; - } - - /** - * Returns the time elapsed since the beginning of the scrolling. - * - * @return The elapsed time in milliseconds. - */ - public int timePassed() { - return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); - } - - /** - * Sets the final position (X) for this scroller. - * - * @param newX The new X offset as an absolute distance from the origin. - * @see #extendDuration(int) - * @see #setFinalY(int) - */ - public void setFinalX(int newX) { - mFinalX = newX; - mDeltaX = mFinalX - mStartX; - mFinished = false; - } - - /** - * Sets the final position (Y) for this scroller. - * - * @param newY The new Y offset as an absolute distance from the origin. - * @see #extendDuration(int) - * @see #setFinalX(int) - */ - public void setFinalY(int newY) { - mFinalY = newY; - mDeltaY = mFinalY - mStartY; - mFinished = false; - } - - /** - * @hide - */ - public boolean isScrollingInDirection(float xvel, float yvel) { - return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX) && - Math.signum(yvel) == Math.signum(mFinalY - mStartY); } } diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java index 462eadb34..0028f97cd 100644 --- a/src/com/android/launcher3/MainProcessInitializer.java +++ b/src/com/android/launcher3/MainProcessInitializer.java @@ -20,14 +20,15 @@ import android.content.Context; import com.android.launcher3.graphics.IconShapeOverride; import com.android.launcher3.logging.FileLog; +import com.android.launcher3.util.ResourceBasedOverride; /** * Utility class to handle one time initializations of the main process */ -public class MainProcessInitializer { +public class MainProcessInitializer implements ResourceBasedOverride { public static void initialize(Context context) { - Utilities.getOverrideObject( + Overrides.getObject( MainProcessInitializer.class, context, R.string.main_process_initializer_class) .init(context); } diff --git a/src/com/android/launcher3/OverviewButtonClickListener.java b/src/com/android/launcher3/OverviewButtonClickListener.java deleted file mode 100644 index dd670d2a7..000000000 --- a/src/com/android/launcher3/OverviewButtonClickListener.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.android.launcher3; - -import android.view.View; - -import com.android.launcher3.userevent.nano.LauncherLogProto.Action; - -/** - * A specialized listener for Overview buttons where both clicks and long clicks are logged - * handled the same via {@link #handleViewClick(View)}. - */ -public abstract class OverviewButtonClickListener implements View.OnClickListener, - View.OnLongClickListener { - - private int mControlType; /** ControlType enum as defined in {@link Action.Touch} */ - - public OverviewButtonClickListener(int controlType) { - mControlType = controlType; - } - - public void attachTo(View v) { - v.setOnClickListener(this); - v.setOnLongClickListener(this); - } - - @Override - public void onClick(View view) { - if (shouldPerformClick(view)) { - handleViewClick(view, Action.Touch.TAP); - } - } - - @Override - public boolean onLongClick(View view) { - if (shouldPerformClick(view)) { - handleViewClick(view, Action.Touch.LONGPRESS); - } - return true; - } - - private boolean shouldPerformClick(View view) { - return !Launcher.getLauncher(view.getContext()).getWorkspace().isSwitchingState(); - } - - private void handleViewClick(View view, int action) { - handleViewClick(view); - Launcher.getLauncher(view.getContext()).getUserEventDispatcher() - .logActionOnControl(action, mControlType); - } - - public abstract void handleViewClick(View view); -}
\ No newline at end of file diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java index 32c198ab0..8589b7ee6 100644 --- a/src/com/android/launcher3/SettingsActivity.java +++ b/src/com/android/launcher3/SettingsActivity.java @@ -24,6 +24,7 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; +import android.app.Fragment; import android.app.FragmentManager; import android.content.ComponentName; import android.content.ContentResolver; @@ -53,7 +54,8 @@ import java.util.Objects; /** * Settings activity for Launcher. Currently implements the following setting: Allow rotation */ -public class SettingsActivity extends Activity { +public class SettingsActivity extends Activity + implements PreferenceFragment.OnPreferenceStartFragmentCallback { private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging"; /** Hidden field Settings.Secure.NOTIFICATION_BADGING */ @@ -71,9 +73,10 @@ public class SettingsActivity extends Activity { super.onCreate(savedInstanceState); if (savedInstanceState == null) { + Fragment f = Fragment.instantiate(this, getString(R.string.settings_fragment_name)); // Display the fragment as the main content. getFragmentManager().beginTransaction() - .replace(android.R.id.content, getNewFragment()) + .replace(android.R.id.content, f) .commit(); } } @@ -82,6 +85,22 @@ public class SettingsActivity extends Activity { return new LauncherSettingsFragment(); } + @Override + public boolean onPreferenceStartFragment( + PreferenceFragment preferenceFragment, Preference pref) { + Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras()); + if (f instanceof DialogFragment) { + ((DialogFragment) f).show(getFragmentManager(), pref.getKey()); + } else { + getFragmentManager() + .beginTransaction() + .replace(android.R.id.content, f) + .addToBackStack(pref.getKey()) + .commit(); + } + return true; + } + /** * This fragment shows the launcher preferences. */ diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 7fe8d35cb..7c5bb1af8 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import android.app.ActivityManager; import android.app.WallpaperManager; import android.content.ComponentName; import android.content.Context; @@ -55,7 +56,6 @@ import com.android.launcher3.config.FeatureFlags; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.HashSet; @@ -131,6 +131,9 @@ public final class Utilities { CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); + public static final boolean IS_RUNNING_IN_TEST_HARNESS = + ActivityManager.isRunningInTestHarness(); + public static boolean isPropertyEnabled(String propertyName) { return Log.isLoggable(propertyName, Log.VERBOSE); } @@ -581,25 +584,6 @@ public final class Utilities { || e.getCause() instanceof DeadObjectException; } - public static <T> T getOverrideObject(Class<T> clazz, Context context, int resId) { - String className = context.getString(resId); - if (!TextUtils.isEmpty(className)) { - try { - Class<?> cls = Class.forName(className); - return (T) cls.getDeclaredConstructor(Context.class).newInstance(context); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException - | ClassCastException | NoSuchMethodException | InvocationTargetException e) { - Log.e(TAG, "Bad overriden class", e); - } - } - - try { - return clazz.newInstance(); - } catch (InstantiationException|IllegalAccessException e) { - throw new RuntimeException(e); - } - } - /** * Returns a HashSet with a single element. We use this instead of Collections.singleton() * because HashSet ensures all operations, such as remove, are supported. diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 54d29c115..a344a7b13 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1036,7 +1036,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator> } protected void onScrollInteractionBegin() { - super.onScrollInteractionEnd(); + super.onScrollInteractionBegin(); mScrollInteractionBegan = true; } @@ -3036,16 +3036,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator> }); } - public View getViewForTag(final Object tag) { - return getFirstMatch(new ItemOperator() { - - @Override - public boolean evaluate(ItemInfo info, View v) { - return info == tag; - } - }); - } - public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) { return (LauncherAppWidgetHostView) getFirstMatch(new ItemOperator() { diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java index 3c3c4066d..76b25655c 100644 --- a/src/com/android/launcher3/allapps/DiscoveryBounce.java +++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java @@ -125,7 +125,8 @@ public class DiscoveryBounce extends AbstractFloatingView { private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) { if (!launcher.isInState(NORMAL) - || launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false) + || (launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false) + && !shouldShowForWorkProfile(launcher)) || AbstractFloatingView.getTopOpenView(launcher) != null || UserManagerCompat.getInstance(launcher).isDemoUser() || ActivityManager.isRunningInTestHarness()) { @@ -149,7 +150,8 @@ public class DiscoveryBounce extends AbstractFloatingView { || !launcher.hasBeenResumed() || launcher.isForceInvisible() || launcher.getDeviceProfile().isVerticalBarLayout() - || launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false) + || (launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false) + && !shouldShowForWorkProfile(launcher)) || UserManagerCompat.getInstance(launcher).isDemoUser() || ActivityManager.isRunningInTestHarness()) { return; @@ -189,4 +191,10 @@ public class DiscoveryBounce extends AbstractFloatingView { mController.setProgress(progress - mDelta); } } + + private static boolean shouldShowForWorkProfile(Launcher launcher) { + return !launcher.getSharedPrefs().getBoolean( + PersonalWorkSlidingTabStrip.KEY_SHOWED_PEEK_WORK_TAB, false) + && UserManagerCompat.getInstance(launcher).hasWorkProfile(); + } } diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java index a916697bf..31c5914b8 100644 --- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java +++ b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java @@ -39,7 +39,7 @@ public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageInd private static final int POSITION_PERSONAL = 0; private static final int POSITION_WORK = 1; - private static final String KEY_SHOWED_PEEK_WORK_TAB = "showed_peek_work_tab"; + public static final String KEY_SHOWED_PEEK_WORK_TAB = "showed_peek_work_tab"; private final Paint mSelectedIndicatorPaint; private final Paint mDividerPaint; diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java index 0c78381ea..29fc2bc60 100644 --- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java +++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java @@ -16,11 +16,14 @@ package com.android.launcher3.compat; +import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import com.android.launcher3.Utilities; + public class AccessibilityManagerCompat { public static boolean isAccessibilityEnabled(Context context) { @@ -44,4 +47,19 @@ public class AccessibilityManagerCompat { private static AccessibilityManager getManager(Context context) { return (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); } + + public static void sendEventToTest(Context context, String eventTag) { + if (!Utilities.IS_RUNNING_IN_TEST_HARNESS) return; + + final AccessibilityManager accessibilityManager = getManager(context); + if (accessibilityManager.isEnabled() && + accessibilityManager.getEnabledAccessibilityServiceList( + AccessibilityServiceInfo.FEEDBACK_ALL_MASK).size() == 0) { + + final AccessibilityEvent e = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_ANNOUNCEMENT); + e.setClassName(eventTag); + accessibilityManager.sendAccessibilityEvent(e); + } + } } diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java index 03e3861ea..ad5147734 100644 --- a/src/com/android/launcher3/compat/UserManagerCompat.java +++ b/src/com/android/launcher3/compat/UserManagerCompat.java @@ -64,4 +64,6 @@ public abstract class UserManagerCompat { public abstract boolean isDemoUser(); public abstract boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user); public abstract boolean isAnyProfileQuietModeEnabled(); + + public abstract boolean hasWorkProfile(); } diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java index 1ff69818a..eec343849 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatVL.java +++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java @@ -120,6 +120,16 @@ public class UserManagerCompatVL extends UserManagerCompat { } @Override + public boolean hasWorkProfile() { + synchronized (this) { + if (mUsers != null) { + return mUsers.size() > 1; + } + } + return getUserProfiles().size() > 1; + } + + @Override public CharSequence getBadgedLabelForUser(CharSequence label, UserHandle user) { if (user == null) { return label; diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java index 1e84b416b..e204c6301 100644 --- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java +++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java @@ -106,6 +106,10 @@ public abstract class BaseItemDragListener extends InternalStateHandler implemen } protected boolean onDragStart(DragEvent event) { + return onDragStart(event, this); + } + + protected boolean onDragStart(DragEvent event, DragOptions.PreDragCondition preDragCondition) { ClipDescription desc = event.getClipDescription(); if (desc == null || !desc.hasMimeType(getMimeType())) { Log.e(TAG, "Someone started a dragAndDrop before us."); @@ -115,7 +119,7 @@ public abstract class BaseItemDragListener extends InternalStateHandler implemen Point downPos = new Point((int) event.getX(), (int) event.getY()); DragOptions options = new DragOptions(); options.systemDndStartPoint = downPos; - options.preDragCondition = this; + options.preDragCondition = preDragCondition; // We use drag event position as the screenPos for the preview image. Since mPreviewRect // already includes the view position relative to the drag event on the source window, @@ -123,7 +127,7 @@ public abstract class BaseItemDragListener extends InternalStateHandler implemen // across windows, using drag position here give a good estimate for relative position // to source window. createDragHelper().startDrag(new Rect(mPreviewRect), - mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options); + mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options); mDragStartTime = SystemClock.uptimeMillis(); return true; } diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 8a216fc17..47bbbcb0d 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -16,6 +16,7 @@ package com.android.launcher3.dragndrop; +import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherState.NORMAL; @@ -31,6 +32,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; import com.android.launcher3.ItemInfo; @@ -145,6 +147,7 @@ public class DragController implements DragDriver.EventListener, TouchController // Hide soft keyboard, if visible UiThreadHelper.hideKeyboardAsync(mLauncher, mWindowToken); + AbstractFloatingView.closeOpenViews(mLauncher, false, TYPE_DISCOVERY_BOUNCE); mOptions = options; if (mOptions.systemDndStartPoint != null) { diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 6b13da70c..6a3ebcfb0 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -25,6 +25,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.annotation.SuppressLint; import android.content.Context; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.text.InputType; @@ -191,14 +192,9 @@ public class Folder extends AbstractFloatingView implements DragSource, public Folder(Context context, AttributeSet attrs) { super(context, attrs); setAlwaysDrawnWithCacheEnabled(false); - Resources res = getResources(); - if (sDefaultFolderName == null) { - sDefaultFolderName = res.getString(R.string.folder_name); - } - if (sHintText == null) { - sHintText = res.getString(R.string.folder_hint_text); - } + setLocaleDependentFields(getResources(), false /* force */); + mLauncher = Launcher.getLauncher(context); // We need this view to be focusable in touch mode so that when text editing of the folder // name is complete, we have something to focus on, thus hiding the cursor and giving @@ -1473,4 +1469,13 @@ public class Folder extends AbstractFloatingView implements DragSource, } return false; } + + public static void setLocaleDependentFields(Resources res, boolean force) { + if (sDefaultFolderName == null || force) { + sDefaultFolderName = res.getString(R.string.folder_name); + } + if (sHintText == null || force) { + sHintText = res.getString(R.string.folder_hint_text); + } + } } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index cb5d872ab..95a6bbd20 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -77,7 +77,6 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Thunk Launcher mLauncher; @Thunk Folder mFolder; private FolderInfo mInfo; - @Thunk static boolean sStaticValuesDirty = true; private CheckLongPressHelper mLongPressHelper; private StylusEventHelper mStylusEventHelper; @@ -185,12 +184,6 @@ public class FolderIcon extends FrameLayout implements FolderListener { return icon; } - @Override - protected Parcelable onSaveInstanceState() { - sStaticValuesDirty = true; - return super.onSaveInstanceState(); - } - public Folder getFolder() { return mFolder; } diff --git a/src/com/android/launcher3/graphics/BitmapRenderer.java b/src/com/android/launcher3/graphics/BitmapRenderer.java index 3d11c448a..2a7f20ea3 100644 --- a/src/com/android/launcher3/graphics/BitmapRenderer.java +++ b/src/com/android/launcher3/graphics/BitmapRenderer.java @@ -23,32 +23,30 @@ import android.os.Build; import com.android.launcher3.Utilities; -public class BitmapRenderer { - - public static final boolean USE_HARDWARE_BITMAP = Utilities.ATLEAST_P; - - public static Bitmap createSoftwareBitmap(int width, int height, Renderer renderer) { - Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - renderer.draw(new Canvas(result)); - return result; - } - - @TargetApi(Build.VERSION_CODES.P) - public static Bitmap createHardwareBitmap(int width, int height, Renderer renderer) { - if (!USE_HARDWARE_BITMAP) { - return createSoftwareBitmap(width, height, renderer); - } - - Picture picture = new Picture(); - renderer.draw(picture.beginRecording(width, height)); - picture.endRecording(); - return Bitmap.createBitmap(picture); - } - - /** - * Interface representing a bitmap draw operation. - */ - public interface Renderer { - void draw(Canvas out); - } +/** + * Interface representing a bitmap draw operation. + */ +public interface BitmapRenderer { + + boolean USE_HARDWARE_BITMAP = Utilities.ATLEAST_P; + + static Bitmap createSoftwareBitmap(int width, int height, BitmapRenderer renderer) { + Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + renderer.draw(new Canvas(result)); + return result; + } + + @TargetApi(Build.VERSION_CODES.P) + static Bitmap createHardwareBitmap(int width, int height, BitmapRenderer renderer) { + if (!USE_HARDWARE_BITMAP) { + return createSoftwareBitmap(width, height, renderer); + } + + Picture picture = new Picture(); + renderer.draw(picture.beginRecording(width, height)); + picture.endRecording(); + return Bitmap.createBitmap(picture); + } + + void draw(Canvas out); } diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java index 34a4e2d77..bbc013d50 100644 --- a/src/com/android/launcher3/graphics/DrawableFactory.java +++ b/src/com/android/launcher3/graphics/DrawableFactory.java @@ -36,11 +36,12 @@ import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsBackgroundDrawable; +import com.android.launcher3.util.ResourceBasedOverride; /** * Factory for creating new drawables. */ -public class DrawableFactory { +public class DrawableFactory implements ResourceBasedOverride { private static final String TAG = "DrawableFactory"; @@ -52,7 +53,7 @@ public class DrawableFactory { public static DrawableFactory get(Context context) { synchronized (LOCK) { if (sInstance == null) { - sInstance = Utilities.getOverrideObject(DrawableFactory.class, + sInstance = Overrides.getObject(DrawableFactory.class, context.getApplicationContext(), R.string.drawable_factory_class); } return sInstance; diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java index 83593aaa0..1c4327c49 100644 --- a/src/com/android/launcher3/logging/LoggerUtils.java +++ b/src/com/android/launcher3/logging/LoggerUtils.java @@ -144,7 +144,7 @@ public class LoggerUtils { } public static Target newItemTarget(View v, InstantAppResolver instantAppResolver) { - return (v.getTag() instanceof ItemInfo) + return (v != null) && (v.getTag() instanceof ItemInfo) ? newItemTarget((ItemInfo) v.getTag(), instantAppResolver) : newTarget(Target.Type.ITEM); } diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java index d1e1051ee..7087fdb78 100644 --- a/src/com/android/launcher3/logging/UserEventDispatcher.java +++ b/src/com/android/launcher3/logging/UserEventDispatcher.java @@ -37,7 +37,6 @@ import android.util.Log; import android.view.View; import android.view.ViewParent; -import com.android.launcher3.DeviceProfile; import com.android.launcher3.DropTarget; import com.android.launcher3.ItemInfo; import com.android.launcher3.R; @@ -51,6 +50,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.LogConfig; +import com.android.launcher3.util.ResourceBasedOverride; import java.util.Locale; import java.util.UUID; @@ -61,7 +61,7 @@ import java.util.UUID; * * $ adb shell setprop log.tag.UserEvent VERBOSE */ -public class UserEventDispatcher { +public class UserEventDispatcher implements ResourceBasedOverride { private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5; @@ -70,7 +70,7 @@ public class UserEventDispatcher { FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT); private static final String UUID_STORAGE = "uuid"; - public static UserEventDispatcher newInstance(Context context, DeviceProfile dp, + public static UserEventDispatcher newInstance(Context context, UserEventDelegate delegate) { SharedPreferences sharedPrefs = Utilities.getDevicePrefs(context); String uuidStr = sharedPrefs.getString(UUID_STORAGE, null); @@ -78,18 +78,16 @@ public class UserEventDispatcher { uuidStr = UUID.randomUUID().toString(); sharedPrefs.edit().putString(UUID_STORAGE, uuidStr).apply(); } - UserEventDispatcher ued = Utilities.getOverrideObject(UserEventDispatcher.class, + UserEventDispatcher ued = Overrides.getObject(UserEventDispatcher.class, context.getApplicationContext(), R.string.user_event_dispatcher_class); ued.mDelegate = delegate; - ued.mIsInLandscapeMode = dp.isVerticalBarLayout(); - ued.mIsInMultiWindowMode = dp.isMultiWindowMode; ued.mUuidStr = uuidStr; ued.mInstantAppResolver = InstantAppResolver.newInstance(context); return ued; } - public static UserEventDispatcher newInstance(Context context, DeviceProfile dp) { - return newInstance(context, dp, null); + public static UserEventDispatcher newInstance(Context context) { + return newInstance(context, null); } public interface UserEventDelegate { @@ -139,8 +137,6 @@ public class UserEventDispatcher { private long mElapsedContainerMillis; private long mElapsedSessionMillis; private long mActionDurationMillis; - private boolean mIsInMultiWindowMode; - private boolean mIsInLandscapeMode; private String mUuidStr; protected InstantAppResolver mInstantAppResolver; private boolean mAppOrTaskLaunch; @@ -434,8 +430,6 @@ public class UserEventDispatcher { public void dispatchUserEvent(LauncherEvent ev, Intent intent) { mAppOrTaskLaunch = false; - ev.isInLandscapeMode = mIsInLandscapeMode; - ev.isInMultiWindowMode = mIsInMultiWindowMode; ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis; ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis; @@ -455,8 +449,6 @@ public class UserEventDispatcher { ev.elapsedContainerMillis, ev.elapsedSessionMillis, ev.actionDurationMillis); - log += "\n isInLandscapeMode " + ev.isInLandscapeMode; - log += "\n isInMultiWindowMode " + ev.isInMultiWindowMode; log += "\n\n"; Log.d(TAG, log); } diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 06da843f7..e82c8f100 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -478,15 +478,11 @@ public class LoaderTask implements Runnable { } info = new ShortcutInfo(pinnedShortcut, context); final ShortcutInfo finalInfo = info; - Provider<Bitmap> fallbackIconProvider = new Provider<Bitmap>() { - @Override - public Bitmap get() { - // If the pinned deep shortcut is no longer published, - // use the last saved icon instead of the default. - return c.loadIcon(finalInfo) - ? finalInfo.iconBitmap : null; - } - }; + // If the pinned deep shortcut is no longer published, + // use the last saved icon instead of the default. + Provider<Bitmap> fallbackIconProvider = () -> + c.loadIcon(finalInfo) ? finalInfo.iconBitmap : null; + LauncherIcons li = LauncherIcons.obtain(context); li.createShortcutIcon(pinnedShortcut, true /* badged */, fallbackIconProvider).applyTo(info); diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java index b527b6a8d..ac5aaf853 100644 --- a/src/com/android/launcher3/notification/NotificationListener.java +++ b/src/com/android/launcher3/notification/NotificationListener.java @@ -197,7 +197,7 @@ public class NotificationListener extends NotificationListenerService { mNotificationBadgingObserver = new SettingsObserver.Secure(getContentResolver()) { @Override public void onSettingChanged(boolean isNotificationBadgingEnabled) { - if (!isNotificationBadgingEnabled) { + if (!isNotificationBadgingEnabled && sIsConnected) { requestUnbind(); } } diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java index be666a6e2..e157482c6 100644 --- a/src/com/android/launcher3/popup/ArrowPopup.java +++ b/src/com/android/launcher3/popup/ArrowPopup.java @@ -36,8 +36,10 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.FrameLayout; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.R; @@ -47,6 +49,7 @@ import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.graphics.TriangleShape; import com.android.launcher3.util.Themes; +import com.android.launcher3.views.BaseDragLayer; import java.util.ArrayList; import java.util.Collections; @@ -134,7 +137,7 @@ public abstract class ArrowPopup extends AbstractFloatingView { protected void reorderAndShow(int viewsToFlip) { setVisibility(View.INVISIBLE); mIsOpen = true; - mLauncher.getDragLayer().addView(this); + getPopupContainer().addView(this); orientAboutObject(); boolean reverseOrder = mIsAboveIcon; @@ -163,7 +166,7 @@ public abstract class ArrowPopup extends AbstractFloatingView { ? R.dimen.popup_arrow_horizontal_center_start : R.dimen.popup_arrow_horizontal_center_end); final int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2; - mLauncher.getDragLayer().addView(mArrow); + getPopupContainer().addView(mArrow); DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams(); if (mIsLeftAligned) { mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth); @@ -179,7 +182,7 @@ public abstract class ArrowPopup extends AbstractFloatingView { ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create( arrowLp.width, arrowLp.height, !mIsAboveIcon)); Paint arrowPaint = arrowDrawable.getPaint(); - arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary)); + arrowPaint.setColor(Themes.getAttrColor(getContext(), R.attr.popupColorPrimary)); // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable. int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius); arrowPaint.setPathEffect(new CornerPathEffect(radius)); @@ -222,7 +225,7 @@ public abstract class ArrowPopup extends AbstractFloatingView { int height = getMeasuredHeight() + extraVerticalSpace; getTargetObjectLocation(mTempRect); - DragLayer dragLayer = mLauncher.getDragLayer(); + InsettableFrameLayout dragLayer = getPopupContainer(); Rect insets = dragLayer.getInsets(); // Align left (right in RTL) if there is room. @@ -301,12 +304,11 @@ public abstract class ArrowPopup extends AbstractFloatingView { return; } - DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams(); - DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams(); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); + FrameLayout.LayoutParams arrowLp = (FrameLayout.LayoutParams) mArrow.getLayoutParams(); if (mIsAboveIcon) { arrowLp.gravity = lp.gravity = Gravity.BOTTOM; - lp.bottomMargin = - mLauncher.getDragLayer().getHeight() - y - getMeasuredHeight() - insets.top; + lp.bottomMargin = getPopupContainer().getHeight() - y - getMeasuredHeight() - insets.top; arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrayOffset - insets.bottom; } else { arrowLp.gravity = lp.gravity = Gravity.TOP; @@ -320,7 +322,7 @@ public abstract class ArrowPopup extends AbstractFloatingView { super.onLayout(changed, l, t, r, b); // enforce contained is within screen - DragLayer dragLayer = mLauncher.getDragLayer(); + ViewGroup dragLayer = getPopupContainer(); if (getTranslationX() + l < 0 || getTranslationX() + r > dragLayer.getWidth()) { // If we are still off screen, center horizontally too. mGravity |= Gravity.CENTER_HORIZONTAL; @@ -454,7 +456,11 @@ public abstract class ArrowPopup extends AbstractFloatingView { } mIsOpen = false; mDeferContainerRemoval = false; - mLauncher.getDragLayer().removeView(this); - mLauncher.getDragLayer().removeView(mArrow); + getPopupContainer().removeView(this); + getPopupContainer().removeView(mArrow); + } + + protected BaseDragLayer getPopupContainer() { + return mLauncher.getDragLayer(); } } diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 172cf41e0..635e04371 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -55,7 +55,6 @@ import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate; import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.dragndrop.DragController; -import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.logging.LoggerUtils; @@ -65,8 +64,10 @@ import com.android.launcher3.notification.NotificationKeyData; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; +import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.views.BaseDragLayer; import java.util.ArrayList; import java.util.List; @@ -146,10 +147,14 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, command, mOriginalIcon, ContainerType.DEEPSHORTCUTS); } + public OnClickListener getItemClickListener() { + return ItemClickHandler.INSTANCE; + } + @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { - DragLayer dl = mLauncher.getDragLayer(); + BaseDragLayer dl = getPopupContainer(); if (!dl.isEventOverView(this, ev)) { mLauncher.getUserEventDispatcher().logActionTapOutside( LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS)); @@ -215,7 +220,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, } @TargetApi(Build.VERSION_CODES.P) - private void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds, + protected void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds, final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) { mNumNotifications = notificationKeys.size(); mOriginalIcon = originalIcon; @@ -293,7 +298,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, @Override protected void getTargetObjectLocation(Rect outPos) { - mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos); + getPopupContainer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos); outPos.top += mOriginalIcon.getPaddingTop(); outPos.left += mOriginalIcon.getPaddingLeft(); outPos.right -= mOriginalIcon.getPaddingRight(); diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java index 9ad266bbc..7d0ea7bb9 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java @@ -121,7 +121,7 @@ public class DeepShortcutView extends FrameLayout { mBubbleText.setText(usingLongLabel ? longLabel : mDetail.getShortLabel()); // TODO: Add the click handler to this view directly and not the child view. - mBubbleText.setOnClickListener(ItemClickHandler.INSTANCE); + mBubbleText.setOnClickListener(container.getItemClickListener()); mBubbleText.setOnLongClickListener(container); mBubbleText.setOnTouchListener(container); } diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index 55f850c8d..0e277eabc 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -43,6 +43,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; @@ -515,6 +516,8 @@ public abstract class AbstractStateChangeTouchController logReachedState(logAction, targetState); } mLauncher.getStateManager().goToState(targetState, false /* animated */); + + AccessibilityManagerCompat.sendEventToTest(mLauncher, "TAPL_WENT_TO_STATE"); } } diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index f2f5592e5..97f836f4d 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -162,7 +162,7 @@ public class ItemClickHandler { * * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}. */ - private static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher) { + public static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher) { if (shortcut.isDisabled()) { final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK; if ((disabledFlags & diff --git a/src/com/android/launcher3/util/FloatRange.java b/src/com/android/launcher3/util/FloatRange.java deleted file mode 100644 index 12772f365..000000000 --- a/src/com/android/launcher3/util/FloatRange.java +++ /dev/null @@ -1,39 +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.util; - -/** - * A mutable class for describing the range of two int values. - */ -public class FloatRange { - - public float start, end; - - public FloatRange() { } - - public FloatRange(float s, float e) { - set(s, e); - } - - public void set(float s, float e) { - start = s; - end = e; - } - - public boolean contains(float value) { - return value >= start && value <= end; - } -} diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java index 4485427f5..5dc7af855 100644 --- a/src/com/android/launcher3/util/InstantAppResolver.java +++ b/src/com/android/launcher3/util/InstantAppResolver.java @@ -23,7 +23,6 @@ import android.util.Log; import com.android.launcher3.AppInfo; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import java.util.Collections; import java.util.List; @@ -31,10 +30,10 @@ import java.util.List; /** * A wrapper class to access instant app related APIs. */ -public class InstantAppResolver { +public class InstantAppResolver implements ResourceBasedOverride { public static InstantAppResolver newInstance(Context context) { - return Utilities.getOverrideObject( + return Overrides.getObject( InstantAppResolver.class, context, R.string.instant_app_resolver_class); } diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java index daedaef91..19cf6c1e5 100644 --- a/src/com/android/launcher3/util/ItemInfoMatcher.java +++ b/src/com/android/launcher3/util/ItemInfoMatcher.java @@ -18,7 +18,6 @@ package com.android.launcher3.util; import android.content.ComponentName; import android.os.UserHandle; -import android.util.SparseLongArray; import com.android.launcher3.FolderInfo; import com.android.launcher3.ItemInfo; @@ -32,14 +31,14 @@ import java.util.HashSet; /** * A utility class to check for {@link ItemInfo} */ -public abstract class ItemInfoMatcher { +public interface ItemInfoMatcher { - public abstract boolean matches(ItemInfo info, ComponentName cn); + boolean matches(ItemInfo info, ComponentName cn); /** * Filters {@param infos} to those satisfying the {@link #matches(ItemInfo, ComponentName)}. */ - public final HashSet<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos) { + default HashSet<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos) { HashSet<ItemInfo> filtered = new HashSet<>(); for (ItemInfo i : infos) { if (i instanceof ShortcutInfo) { @@ -70,88 +69,43 @@ public abstract class ItemInfoMatcher { /** * Returns a new matcher with returns true if either this or {@param matcher} returns true. */ - public ItemInfoMatcher or(final ItemInfoMatcher matcher) { - final ItemInfoMatcher that = this; - return new ItemInfoMatcher() { - @Override - public boolean matches(ItemInfo info, ComponentName cn) { - return that.matches(info, cn) || matcher.matches(info, cn); - } - }; + default ItemInfoMatcher or(ItemInfoMatcher matcher) { + return (info, cn) -> matches(info, cn) || matcher.matches(info, cn); } /** * Returns a new matcher with returns true if both this and {@param matcher} returns true. */ - public ItemInfoMatcher and(final ItemInfoMatcher matcher) { - final ItemInfoMatcher that = this; - return new ItemInfoMatcher() { - @Override - public boolean matches(ItemInfo info, ComponentName cn) { - return that.matches(info, cn) && matcher.matches(info, cn); - } - }; + default ItemInfoMatcher and(ItemInfoMatcher matcher) { + return (info, cn) -> matches(info, cn) && matcher.matches(info, cn); } /** * Returns a new matcher which returns the opposite boolean value of the provided * {@param matcher}. */ - public static ItemInfoMatcher not(final ItemInfoMatcher matcher) { - return new ItemInfoMatcher() { - @Override - public boolean matches(ItemInfo info, ComponentName cn) { - return !matcher.matches(info, cn); - } - }; + static ItemInfoMatcher not(ItemInfoMatcher matcher) { + return (info, cn) -> !matcher.matches(info, cn); } - public static ItemInfoMatcher ofUser(final UserHandle user) { - return new ItemInfoMatcher() { - @Override - public boolean matches(ItemInfo info, ComponentName cn) { - return info.user.equals(user); - } - }; + static ItemInfoMatcher ofUser(UserHandle user) { + return (info, cn) -> info.user.equals(user); } - public static ItemInfoMatcher ofComponents( - final HashSet<ComponentName> components, final UserHandle user) { - return new ItemInfoMatcher() { - @Override - public boolean matches(ItemInfo info, ComponentName cn) { - return components.contains(cn) && info.user.equals(user); - } - }; + static ItemInfoMatcher ofComponents(HashSet<ComponentName> components, UserHandle user) { + return (info, cn) -> components.contains(cn) && info.user.equals(user); } - public static ItemInfoMatcher ofPackages( - final HashSet<String> packageNames, final UserHandle user) { - return new ItemInfoMatcher() { - @Override - public boolean matches(ItemInfo info, ComponentName cn) { - return packageNames.contains(cn.getPackageName()) && info.user.equals(user); - } - }; + static ItemInfoMatcher ofPackages(HashSet<String> packageNames, UserHandle user) { + return (info, cn) -> packageNames.contains(cn.getPackageName()) && info.user.equals(user); } - public static ItemInfoMatcher ofShortcutKeys(final HashSet<ShortcutKey> keys) { - return new ItemInfoMatcher() { - @Override - public boolean matches(ItemInfo info, ComponentName cn) { - return info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT && + static ItemInfoMatcher ofShortcutKeys(HashSet<ShortcutKey> keys) { + return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT && keys.contains(ShortcutKey.fromItemInfo(info)); - } - }; } - public static ItemInfoMatcher ofItemIds( - final LongArrayMap<Boolean> ids, final Boolean matchDefault) { - return new ItemInfoMatcher() { - @Override - public boolean matches(ItemInfo info, ComponentName cn) { - return ids.get(info.id, matchDefault); - } - }; + static ItemInfoMatcher ofItemIds(LongArrayMap<Boolean> ids, Boolean matchDefault) { + return (info, cn) -> ids.get(info.id, matchDefault); } } diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java new file mode 100644 index 000000000..5747db1c1 --- /dev/null +++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util; + +import android.content.Context; +import android.os.Looper; + +import com.android.launcher3.MainThreadExecutor; + +import java.util.concurrent.ExecutionException; + +/** + * Utility class for defining singletons which are initiated on main thread. + */ +public class MainThreadInitializedObject<T> { + + private final ObjectProvider<T> mProvider; + private T mValue; + + public MainThreadInitializedObject(ObjectProvider<T> provider) { + mProvider = provider; + } + + public T get(Context context) { + if (mValue == null) { + if (Looper.myLooper() == Looper.getMainLooper()) { + mValue = mProvider.get(context.getApplicationContext()); + } else { + try { + return new MainThreadExecutor().submit(() -> get(context)).get(); + } catch (InterruptedException|ExecutionException e) { + throw new RuntimeException(e); + } + } + } + return mValue; + } + + public T getNoCreate() { + return mValue; + } + + public interface ObjectProvider<T> { + + T get(Context context); + } +} diff --git a/src/com/android/launcher3/util/Provider.java b/src/com/android/launcher3/util/Provider.java index 1cdd8d6aa..4a54c0f05 100644 --- a/src/com/android/launcher3/util/Provider.java +++ b/src/com/android/launcher3/util/Provider.java @@ -19,20 +19,15 @@ package com.android.launcher3.util; /** * Utility class to allow lazy initialization of objects. */ -public abstract class Provider<T> { +public interface Provider<T> { /** * Initializes and returns the object. This may contain expensive operations not suitable * to UI thread. */ - public abstract T get(); + T get(); - public static <T> Provider<T> of (final T value) { - return new Provider<T>() { - @Override - public T get() { - return value; - } - }; + static <T> Provider<T> of (T value) { + return() -> value; } } diff --git a/src/com/android/launcher3/util/ResourceBasedOverride.java b/src/com/android/launcher3/util/ResourceBasedOverride.java new file mode 100644 index 000000000..e2c4992a4 --- /dev/null +++ b/src/com/android/launcher3/util/ResourceBasedOverride.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util; + +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; + +import java.lang.reflect.InvocationTargetException; + +/** + * An interface to indicate that a class is dynamically loaded using resource overlay, hence its + * class name and constructor should be preserved by proguard + */ +public interface ResourceBasedOverride { + + class Overrides { + + private static final String TAG = "Overrides"; + + public static <T extends ResourceBasedOverride> T getObject( + Class<T> clazz, Context context, int resId) { + String className = context.getString(resId); + if (!TextUtils.isEmpty(className)) { + try { + Class<?> cls = Class.forName(className); + return (T) cls.getDeclaredConstructor(Context.class).newInstance(context); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException + | ClassCastException | NoSuchMethodException | InvocationTargetException e) { + Log.e(TAG, "Bad overriden class", e); + } + } + + try { + return clazz.newInstance(); + } catch (InstantiationException|IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java new file mode 100644 index 000000000..04100af7f --- /dev/null +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.views; + +import android.content.Context; +import android.content.ContextWrapper; +import android.view.ContextThemeWrapper; + +/** + * An interface to be used along with a context. This allows a generic class to depend on Context + * subclass instead of an Activity. + */ +public interface ActivityContext { + + default boolean finishAutoCancelActionMode() { + return false; + } + + BaseDragLayer getDragLayer(); + + static ActivityContext lookupContext(Context context) { + if (context instanceof ActivityContext) { + return (ActivityContext) context; + } else if (context instanceof ContextThemeWrapper) { + return lookupContext(((ContextWrapper) context).getBaseContext()); + } else { + throw new IllegalArgumentException("Cannot find ActivityContext in parent tree"); + } + } +} diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index 8457b2bcd..e8a879fcc 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -28,8 +28,6 @@ import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.BaseActivity; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.Utilities; import com.android.launcher3.util.MultiValueAlpha; @@ -41,7 +39,8 @@ import java.util.ArrayList; /** * A viewgroup with utility methods for drag-n-drop and touch interception */ -public abstract class BaseDragLayer<T extends BaseDraggingActivity> extends InsettableFrameLayout { +public abstract class BaseDragLayer<T extends Context & ActivityContext> + extends InsettableFrameLayout { protected final int[] mTmpXY = new int[2]; protected final Rect mHitRect = new Rect(); @@ -55,7 +54,7 @@ public abstract class BaseDragLayer<T extends BaseDraggingActivity> extends Inse public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) { super(context, attrs); - mActivity = (T) BaseActivity.fromContext(context); + mActivity = (T) ActivityContext.lookupContext(context); mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount); } diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index db4c49226..dc6d2ff09 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.graphics.RectF; +import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.ArrayMap; import android.util.AttributeSet; @@ -94,7 +95,7 @@ public class OptionsPopupView extends ArrowPopup if (ev.getAction() != MotionEvent.ACTION_DOWN) { return false; } - if (mLauncher.getDragLayer().isEventOverView(this, ev)) { + if (getPopupContainer().isEventOverView(this, ev)) { return false; } close(true); @@ -133,6 +134,11 @@ public class OptionsPopupView extends ArrowPopup popup.reorderAndShow(popup.getChildCount()); } + @VisibleForTesting + public static OptionsPopupView getOptionsPopup(Launcher launcher) { + return launcher.findViewById(R.id.deep_shortcuts_container); + } + public static void showDefaultOptions(Launcher launcher, float x, float y) { float halfSize = launcher.getResources().getDimension(R.dimen.options_menu_thumb_size) / 2; if (x < 0 || y < 0) { diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java index e94d81d75..b49322832 100644 --- a/src/com/android/launcher3/widget/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java @@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.PropertyValuesHolder; import android.content.Context; import android.graphics.Rect; +import android.support.annotation.VisibleForTesting; import android.util.AttributeSet; import android.util.Pair; import android.view.LayoutInflater; @@ -224,6 +225,11 @@ public class WidgetsFullSheet extends BaseWidgetSheet return sheet; } + @VisibleForTesting + public static WidgetsRecyclerView getWidgetsView(Launcher launcher) { + return launcher.findViewById(R.id.widgets_list_view); + } + @Override protected int getElementsRowCount() { return mAdapter.getItemCount(); |