Merge "Changing the FolderIcon shape based on AdpativeIcon" into ub-launcher3-master
diff --git a/Android.bp b/Android.bp
index aefc1f0..2608280 100644
--- a/Android.bp
+++ b/Android.bp
@@ -23,6 +23,7 @@
srcs: [
"tests/tapl/**/*.java",
"quickstep/src/com/android/quickstep/SwipeUpSetting.java",
+ "src/com/android/launcher3/util/SecureSettingsObserver.java",
"src/com/android/launcher3/TestProtocol.java",
],
platform_apis: true,
diff --git a/Android.mk b/Android.mk
index b8e6c85..fbe19b0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -53,7 +53,6 @@
LOCAL_SDK_VERSION := current
LOCAL_MIN_SDK_VERSION := 28
LOCAL_MODULE := LauncherPluginLib
-LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -67,9 +66,10 @@
LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.recyclerview_recyclerview \
- androidx.dynamicanimation_dynamicanimation
+ androidx.dynamicanimation_dynamicanimation \
+ androidx.preference_preference
-LOCAL_STATIC_JAVA_LIBRARIES := libPluginCore
+LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
LOCAL_SRC_FILES := \
$(call all-proto-files-under, protos) \
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 8f4d5be..5b00b7d 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -156,7 +156,7 @@
The settings activity. To extend point settings_fragment_name to appropriate fragment class
-->
<activity
- android:name="com.android.launcher3.SettingsActivity"
+ android:name="com.android.launcher3.settings.SettingsActivity"
android:label="@string/settings_button_text"
android:theme="@android:style/Theme.DeviceDefault.Settings"
android:autoRemoveFromRecents="true">
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index ca12951..88c362d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -43,7 +43,12 @@
}
public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass) {
- mPluginManager.addPluginListener(listener, pluginClass);
+ addPluginListener(listener, pluginClass, false);
+ }
+
+ public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass,
+ boolean allowMultiple) {
+ mPluginManager.addPluginListener(listener, pluginClass, allowMultiple);
}
public void removePluginListener(PluginListener<? extends Plugin> listener) {
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 4417a3d..94ec69a 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -52,6 +52,7 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.AssistDataReceiver;
import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -80,6 +81,7 @@
private final OverviewCallbacks mOverviewCallbacks;
private final TaskOverlayFactory mTaskOverlayFactory;
private final TouchInteractionLog mTouchInteractionLog;
+ private final InputConsumerController mInputConsumer;
private final boolean mIsDeferredDownTarget;
private final PointF mDownPos = new PointF();
@@ -101,8 +103,8 @@
RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
@HitTarget int downHitTarget, OverviewCallbacks overviewCallbacks,
- TaskOverlayFactory taskOverlayFactory, VelocityTracker velocityTracker,
- TouchInteractionLog touchInteractionLog) {
+ TaskOverlayFactory taskOverlayFactory, InputConsumerController inputConsumer,
+ VelocityTracker velocityTracker, TouchInteractionLog touchInteractionLog) {
super(base);
mRunningTask = runningTaskInfo;
@@ -117,6 +119,7 @@
mTaskOverlayFactory = taskOverlayFactory;
mTouchInteractionLog = touchInteractionLog;
mTouchInteractionLog.setTouchConsumer(this);
+ mInputConsumer = inputConsumer;
}
@Override
@@ -226,7 +229,7 @@
RecentsAnimationState animationState = new RecentsAnimationState();
final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
animationState.id, mRunningTask, this, touchTimeMs, mActivityControlHelper,
- mTouchInteractionLog);
+ mInputConsumer, mTouchInteractionLog);
// Preload the plan
mRecentsModel.loadTasks(mRunningTask.id, null);
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
index d71c08a..27f1399 100644
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -15,23 +15,21 @@
*/
package com.android.quickstep;
+import static com.android.quickstep.SwipeUpSetting.newSwipeUpSettingsObserver;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
-import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
-import android.content.ContentResolver;
import android.content.Context;
-import android.database.ContentObserver;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
-import android.provider.Settings;
import android.util.Log;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SecureSettingsObserver;
import com.android.launcher3.util.UiThreadHelper;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -60,7 +58,7 @@
private static final int MSG_SET_BACK_BUTTON_ALPHA = 201;
private static final int MSG_SET_SWIPE_UP_ENABLED = 202;
- private final SwipeUpGestureEnabledSettingObserver mSwipeUpSettingObserver;
+ private final SecureSettingsObserver mSwipeUpSettingObserver;
private final Context mContext;
private final Handler mUiHandler;
@@ -85,9 +83,11 @@
mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage);
if (SwipeUpSetting.isSwipeUpSettingAvailable()) {
- mSwipeUpSettingObserver = new SwipeUpGestureEnabledSettingObserver(mUiHandler,
- context.getContentResolver());
+ mSwipeUpSettingObserver =
+ newSwipeUpSettingsObserver(context, this::notifySwipeUpSettingChanged);
mSwipeUpSettingObserver.register();
+ mSwipeUpEnabled = mSwipeUpSettingObserver.getValue();
+ resetHomeBounceSeenOnQuickstepEnabledFirstTime();
} else {
mSwipeUpSettingObserver = null;
mSwipeUpEnabled = SwipeUpSetting.isSwipeUpEnabledDefaultValue();
@@ -192,34 +192,6 @@
sendToTarget();
}
- private class SwipeUpGestureEnabledSettingObserver extends ContentObserver {
- private ContentResolver mResolver;
- private final int defaultValue;
-
- SwipeUpGestureEnabledSettingObserver(Handler handler, ContentResolver resolver) {
- super(handler);
- mResolver = resolver;
- defaultValue = SwipeUpSetting.isSwipeUpEnabledDefaultValue() ? 1 : 0;
- }
-
- public void register() {
- mResolver.registerContentObserver(Settings.Secure.getUriFor(SWIPE_UP_SETTING_NAME),
- false, this);
- mSwipeUpEnabled = getValue();
- resetHomeBounceSeenOnQuickstepEnabledFirstTime();
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- notifySwipeUpSettingChanged(getValue());
- }
-
- private boolean getValue() {
- return Settings.Secure.getInt(mResolver, SWIPE_UP_SETTING_NAME, defaultValue) == 1;
- }
- }
-
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
if (mSwipeUpEnabled && !Utilities.getPrefs(mContext).getBoolean(
HAS_ENABLED_QUICKSTEP_ONCE, true)) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
index eea3971..2f3cb5f 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -53,24 +53,18 @@
new LooperExecutor(UiThreadHelper.getBackgroundLooper());
private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
- private InputConsumerController mInputConsumer =
- InputConsumerController.getRecentsAnimationInputConsumer();
+ private final InputConsumerController mInputConsumer;
private final Supplier<TouchConsumer> mTouchProxySupplier;
- private boolean mInputConsumerUnregistered;
- private boolean mTouchProxyEnabled;
-
private TouchConsumer mTouchConsumer;
private boolean mTouchInProgress;
- private boolean mInputConsumerUnregisterPending;
private boolean mFinishPending;
- public RecentsAnimationWrapper(Supplier<TouchConsumer> touchProxySupplier) {
- // Register the input consumer on the UI thread, to ensure that it runs after any pending
- // unregister calls
+ public RecentsAnimationWrapper(InputConsumerController inputConsumer,
+ Supplier<TouchConsumer> touchProxySupplier) {
+ mInputConsumer = inputConsumer;
mTouchProxySupplier = touchProxySupplier;
- mMainThreadExecutor.execute(mInputConsumer::registerInputConsumer);
}
public synchronized void setController(
@@ -109,6 +103,7 @@
public void finish(boolean toHome, Runnable onFinishComplete) {
if (!toHome) {
mExecutorService.submit(() -> finishBg(false, onFinishComplete));
+ return;
}
mMainThreadExecutor.execute(() -> {
@@ -152,28 +147,12 @@
}
}
- public void unregisterInputConsumer() {
- mMainThreadExecutor.execute(this::unregisterInputConsumerUi);
- }
-
- private void unregisterInputConsumerUi() {
- if (mTouchProxyEnabled && mTouchInProgress) {
- mInputConsumerUnregisterPending = true;
- } else {
- mInputConsumerUnregistered = true;
- mInputConsumer.unregisterInputConsumer();
- }
- }
-
public void enableTouchProxy() {
mMainThreadExecutor.execute(this::enableTouchProxyUi);
}
private void enableTouchProxyUi() {
- if (!mInputConsumerUnregistered) {
- mTouchProxyEnabled = true;
- mInputConsumer.setTouchListener(this::onInputConsumerTouch);
- }
+ mInputConsumer.setTouchListener(this::onInputConsumerTouch);
}
private boolean onInputConsumerTouch(MotionEvent ev) {
@@ -184,10 +163,6 @@
} else if (action == ACTION_CANCEL || action == ACTION_UP) {
// Finish any pending actions
mTouchInProgress = false;
- if (mInputConsumerUnregisterPending) {
- mInputConsumerUnregisterPending = false;
- mInputConsumer.unregisterInputConsumer();
- }
if (mFinishPending) {
mFinishPending = false;
mExecutorService.submit(() -> finishBg(true, null));
diff --git a/quickstep/src/com/android/quickstep/SwipeUpSetting.java b/quickstep/src/com/android/quickstep/SwipeUpSetting.java
index 0f91f97..381ab9f 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpSetting.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpSetting.java
@@ -16,9 +16,15 @@
package com.android.quickstep;
+import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
+
+import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
+import com.android.launcher3.util.SecureSettingsObserver;
+import com.android.launcher3.util.SecureSettingsObserver.OnChangeListener;
+
public final class SwipeUpSetting {
private static final String TAG = "SwipeUpSetting";
@@ -47,4 +53,10 @@
public static boolean isSwipeUpEnabledDefaultValue() {
return getSystemBooleanRes(SWIPE_UP_ENABLED_DEFAULT_RES_NAME);
}
+
+ public static SecureSettingsObserver newSwipeUpSettingsObserver(Context context,
+ OnChangeListener listener) {
+ return new SecureSettingsObserver(context.getContentResolver(), listener,
+ SWIPE_UP_SETTING_NAME, isSwipeUpEnabledDefaultValue() ? 1 : 0);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 7289516..59a937f 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -20,21 +20,34 @@
import android.graphics.Matrix;
import android.view.View;
+import androidx.annotation.AnyThread;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import androidx.annotation.AnyThread;
+import java.util.ArrayList;
+import java.util.List;
/**
* Factory class to create and add an overlays on the TaskView
*/
public class TaskOverlayFactory implements ResourceBasedOverride {
-
private static TaskOverlayFactory sInstance;
+ /** Note that these will be shown in order from top to bottom, if available for the task. */
+ private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
+ new TaskSystemShortcut.AppInfo(),
+ new TaskSystemShortcut.SplitScreen(),
+ new TaskSystemShortcut.Pin(),
+ new TaskSystemShortcut.Install(),
+ };
+
public static TaskOverlayFactory get(Context context) {
Preconditions.assertUIThread();
if (sInstance == null) {
@@ -55,9 +68,23 @@
public static class TaskOverlay {
- public void setTaskInfo(Task task, ThumbnailData thumbnail, Matrix matrix) { }
+ public void setTaskInfo(Task task, ThumbnailData thumbnail, Matrix matrix) {
+ }
- public void reset() { }
+ public void reset() {
+ }
+ public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
+ final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
+ final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
+ for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
+ View.OnClickListener onClickListener =
+ menuOption.getOnClickListener(activity, taskView);
+ if (onClickListener != null) {
+ shortcuts.add(menuOption);
+ }
+ }
+ return shortcuts;
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index e64d04a..66ce4c3 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -64,7 +64,7 @@
protected T mSystemShortcut;
protected TaskSystemShortcut(T systemShortcut) {
- super(systemShortcut.iconResId, systemShortcut.labelResId);
+ super(systemShortcut);
mSystemShortcut = systemShortcut;
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index cded799..9371a4c 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -51,6 +51,7 @@
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ChoreographerCompat;
+import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
import java.io.FileDescriptor;
@@ -185,6 +186,7 @@
private OverviewCallbacks mOverviewCallbacks;
private TaskOverlayFactory mTaskOverlayFactory;
private TouchInteractionLog mTouchInteractionLog;
+ private InputConsumerController mInputConsumer;
private Choreographer mMainThreadChoreographer;
private Choreographer mBackgroundThreadChoreographer;
@@ -203,6 +205,8 @@
mOverviewCallbacks = OverviewCallbacks.get(this);
mTaskOverlayFactory = TaskOverlayFactory.get(this);
mTouchInteractionLog = new TouchInteractionLog();
+ mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
+ mInputConsumer.registerInputConsumer();
sConnected = true;
@@ -213,6 +217,7 @@
@Override
public void onDestroy() {
+ mInputConsumer.unregisterInputConsumer();
mOverviewCommandHelper.onDestroy();
sConnected = false;
super.onDestroy();
@@ -256,7 +261,7 @@
mOverviewCommandHelper.overviewIntent,
mOverviewCommandHelper.getActivityControlHelper(), mMainThreadExecutor,
mBackgroundThreadChoreographer, downHitTarget, mOverviewCallbacks,
- mTaskOverlayFactory, tracker, mTouchInteractionLog);
+ mTaskOverlayFactory, mInputConsumer, tracker, mTouchInteractionLog);
}
}
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 9991552..1c79f44 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -78,6 +78,7 @@
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -226,8 +227,7 @@
private @InteractionType int mInteractionType = INTERACTION_NORMAL;
- private final RecentsAnimationWrapper mRecentsAnimationWrapper =
- new RecentsAnimationWrapper(this::createNewTouchProxyHandler);
+ private final RecentsAnimationWrapper mRecentsAnimationWrapper;
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
@@ -241,7 +241,7 @@
WindowTransformSwipeHandler(int id, RunningTaskInfo runningTaskInfo, Context context,
long touchTimeMs, ActivityControlHelper<T> controller,
- TouchInteractionLog touchInteractionLog) {
+ InputConsumerController inputConsumer, TouchInteractionLog touchInteractionLog) {
this.id = id;
mContext = context;
mRunningTaskInfo = runningTaskInfo;
@@ -251,6 +251,8 @@
mActivityInitListener = mActivityControlHelper
.createActivityInitListener(this::onActivityInit);
mTouchInteractionLog = touchInteractionLog;
+ mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer,
+ this::createNewTouchProxyHandler);
initStateCallbacks();
}
@@ -538,7 +540,7 @@
public void updateDisplacement(float displacement) {
// We are moving in the negative x/y direction
displacement = -displacement;
- if (displacement > mTransitionDragLength) {
+ if (displacement > mTransitionDragLength && mTransitionDragLength > 0) {
mCurrentShift.updateValue(1);
if (!mBgLongSwipeMode) {
@@ -813,8 +815,12 @@
long startMillis = SystemClock.uptimeMillis();
executeOnUiThread(() -> {
// Animate the launcher components at the same time as the window, always on UI thread.
- if (mLauncherTransitionController != null && !mWasLauncherAlreadyVisible
- && start != end && duration > 0) {
+ if (mLauncherTransitionController == null) {
+ return;
+ }
+ if (start == end || duration <= 0) {
+ mLauncherTransitionController.getAnimationPlayer().end();
+ } else {
// Adjust start progress and duration in case we are on a different thread.
long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
elapsedMillis = Utilities.boundToRange(elapsedMillis, 0, duration);
@@ -860,7 +866,6 @@
}
mActivityInitListener.unregister();
- mRecentsAnimationWrapper.unregisterInputConsumer();
mTaskSnapshot = null;
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 28928a8..667165b 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -43,6 +43,8 @@
import com.android.quickstep.TaskUtils;
import com.android.quickstep.views.IconView.OnScaleUpdateListener;
+import java.util.List;
+
/**
* Contains options for a recent task when long-pressing its icon.
*/
@@ -50,14 +52,6 @@
private static final Rect sTempRect = new Rect();
- /** Note that these will be shown in order from top to bottom, if available for the task. */
- public static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[] {
- new TaskSystemShortcut.AppInfo(),
- new TaskSystemShortcut.SplitScreen(),
- new TaskSystemShortcut.Pin(),
- new TaskSystemShortcut.Install(),
- };
-
private final OnScaleUpdateListener mTaskViewIconScaleListener = new OnScaleUpdateListener() {
@Override
public void onScaleUpdate(float scale) {
@@ -197,19 +191,21 @@
params.topMargin = (int) -mThumbnailTopMargin;
mTaskIcon.setLayoutParams(params);
- for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
- OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, taskView);
- if (onClickListener != null) {
- addMenuOption(menuOption, onClickListener);
- }
+ final BaseDraggingActivity activity = BaseDraggingActivity.fromContext(getContext());
+ final List<TaskSystemShortcut> shortcuts =
+ taskView.getTaskOverlay().getEnabledShortcuts(taskView);
+ final int count = shortcuts.size();
+ for (int i = 0; i < count; ++i) {
+ final TaskSystemShortcut menuOption = shortcuts.get(i);
+ addMenuOption(menuOption, menuOption.getOnClickListener(activity, taskView));
}
}
private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) {
ViewGroup menuOptionView = (ViewGroup) mActivity.getLayoutInflater().inflate(
R.layout.task_view_menu_option, this, false);
- menuOptionView.findViewById(R.id.icon).setBackgroundResource(menuOption.iconResId);
- ((TextView) menuOptionView.findViewById(R.id.text)).setText(menuOption.labelResId);
+ menuOption.setIconAndLabelFor(
+ menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
menuOptionView.setOnClickListener(onClickListener);
mOptionLayout.addView(menuOptionView);
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 7223f97..ce65de1 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -173,6 +173,10 @@
return 0;
}
+ public TaskOverlay getTaskOverlay() {
+ return mOverlay;
+ }
+
@Override
protected void onDraw(Canvas canvas) {
drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(), mCornerRadius);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index a0615f5..56074f0 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -42,12 +42,12 @@
import android.widget.FrameLayout;
import android.widget.Toast;
-import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.views.RecentsView.PageCallbacks;
@@ -56,8 +56,9 @@
import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-
import com.android.systemui.shared.system.ActivityOptionsCompat;
+
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -190,6 +191,10 @@
return mIconView;
}
+ public TaskOverlayFactory.TaskOverlay getTaskOverlay() {
+ return mSnapshotView.getTaskOverlay();
+ }
+
public void launchTask(boolean animate) {
launchTask(animate, (result) -> {
if (!result) {
@@ -384,11 +389,14 @@
final Context context = getContext();
final BaseDraggingActivity activity = fromContext(context);
- for (TaskSystemShortcut menuOption : TaskMenuView.MENU_OPTIONS) {
+ final List<TaskSystemShortcut> shortcuts =
+ mSnapshotView.getTaskOverlay().getEnabledShortcuts(this);
+ final int count = shortcuts.size();
+ for (int i = 0; i < count; ++i) {
+ final TaskSystemShortcut menuOption = shortcuts.get(i);
OnClickListener onClickListener = menuOption.getOnClickListener(activity, this);
if (onClickListener != null) {
- info.addAction(new AccessibilityNodeInfo.AccessibilityAction(menuOption.labelResId,
- context.getText(menuOption.labelResId)));
+ info.addAction(menuOption.createAccessibilityAction(context));
}
}
@@ -408,8 +416,12 @@
return true;
}
- for (TaskSystemShortcut menuOption : TaskMenuView.MENU_OPTIONS) {
- if (action == menuOption.labelResId) {
+ final List<TaskSystemShortcut> shortcuts =
+ mSnapshotView.getTaskOverlay().getEnabledShortcuts(this);
+ final int count = shortcuts.size();
+ for (int i = 0; i < count; ++i) {
+ final TaskSystemShortcut menuOption = shortcuts.get(i);
+ if (menuOption.hasHandlerForAction(action)) {
OnClickListener onClickListener = menuOption.getOnClickListener(
fromContext(getContext()), this);
if (onClickListener != null) {
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 1df7c2f..c55cc49 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -14,9 +14,10 @@
limitations under the License.
-->
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<androidx.preference.PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
- <com.android.launcher3.views.ButtonPreference
+ <com.android.launcher3.settings.IconBadgingPreference
android:key="pref_icon_badging"
android:title="@string/icon_badging_title"
android:persistent="false"
@@ -27,7 +28,7 @@
android:name=":settings:fragment_args_key"
android:value="notification_badging" />
</intent>
- </com.android.launcher3.views.ButtonPreference>
+ </com.android.launcher3.settings.IconBadgingPreference>
<SwitchPreference
android:key="pref_add_icon_to_home"
@@ -52,10 +53,10 @@
android:defaultValue=""
android:persistent="false" />
- <PreferenceScreen
+ <androidx.preference.PreferenceScreen
android:fragment="com.android.launcher3.config.FlagTogglerPreferenceFragment"
android:key="flag_toggler"
android:persistent="false"
android:title="Feature flags"/>
-</PreferenceScreen>
+</androidx.preference.PreferenceScreen>
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index b02182c..6bf5812 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,7 +16,7 @@
package com.android.launcher3;
-import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
+import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
import android.content.ComponentName;
import android.content.ContentProviderClient;
@@ -33,7 +33,7 @@
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.SettingsObserver;
+import com.android.launcher3.util.SecureSettingsObserver;
public class LauncherAppState {
@@ -48,7 +48,7 @@
private final IconCache mIconCache;
private final WidgetPreviewLoader mWidgetCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
- private final SettingsObserver mNotificationBadgingObserver;
+ private final SecureSettingsObserver mNotificationBadgingObserver;
public static LauncherAppState getInstance(final Context context) {
return INSTANCE.get(context);
@@ -99,17 +99,17 @@
mNotificationBadgingObserver = null;
} else {
// Register an observer to rebind the notification listener when badging is re-enabled.
- mNotificationBadgingObserver = new SettingsObserver.Secure(
- mContext.getContentResolver()) {
- @Override
- public void onSettingChanged(boolean isNotificationBadgingEnabled) {
- if (isNotificationBadgingEnabled) {
- NotificationListener.requestRebind(new ComponentName(
- mContext, NotificationListener.class));
- }
- }
- };
- mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
+ mNotificationBadgingObserver =
+ newNotificationSettingsObserver(mContext, this::onNotificationSettingsChanged);
+ mNotificationBadgingObserver.register();
+ mNotificationBadgingObserver.dispatchOnChange();
+ }
+ }
+
+ protected void onNotificationSettingsChanged(boolean isNotificationBadgingEnabled) {
+ if (isNotificationBadgingEnabled) {
+ NotificationListener.requestRebind(new ComponentName(
+ mContext, NotificationListener.class));
}
}
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
deleted file mode 100644
index 60edcda..0000000
--- a/src/com/android/launcher3/SettingsActivity.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
-import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
-
-import android.annotation.TargetApi;
-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;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.Adapter;
-import android.widget.ListView;
-
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.IconShapeOverride;
-import com.android.launcher3.notification.NotificationListener;
-import com.android.launcher3.util.ListViewHighlighter;
-import com.android.launcher3.util.SettingsObserver;
-import com.android.launcher3.views.ButtonPreference;
-
-import java.util.Objects;
-
-/**
- * Settings activity for Launcher. Currently implements the following setting: Allow rotation
- */
-public class SettingsActivity extends Activity
- implements PreferenceFragment.OnPreferenceStartFragmentCallback {
-
- private static final String FLAGS_PREFERENCE_KEY = "flag_toggler";
-
- private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
- /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
- public static final String NOTIFICATION_BADGING = "notification_badging";
- /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
- private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
-
- private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
- private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
- private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
- private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- 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, f)
- .commit();
- }
- }
-
- protected PreferenceFragment getNewFragment() {
- return new LauncherSettingsFragment();
- }
-
- @Override
- public boolean onPreferenceStartFragment(
- PreferenceFragment preferenceFragment, Preference pref) {
- if (getFragmentManager().isStateSaved()) {
- // Sometimes onClick can come after onPause because of being posted on the handler.
- // Skip starting new fragments in that case.
- return false;
- }
- 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.
- */
- public static class LauncherSettingsFragment extends PreferenceFragment {
-
- private IconBadgingObserver mIconBadgingObserver;
-
- private String mPreferenceKey;
- private boolean mPreferenceHighlighted = false;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (savedInstanceState != null) {
- mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
- }
-
- getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
- addPreferencesFromResource(R.xml.launcher_preferences);
-
- // Only show flag toggler UI if this build variant implements that.
- Preference flagToggler = findPreference(FLAGS_PREFERENCE_KEY);
- if (flagToggler != null && !FeatureFlags.showFlagTogglerUi()) {
- getPreferenceScreen().removePreference(flagToggler);
- }
-
- ContentResolver resolver = getActivity().getContentResolver();
-
- ButtonPreference iconBadgingPref =
- (ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY);
- if (!Utilities.ATLEAST_OREO) {
- getPreferenceScreen().removePreference(
- findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
- getPreferenceScreen().removePreference(iconBadgingPref);
- } else if (!getResources().getBoolean(R.bool.notification_badging_enabled)) {
- getPreferenceScreen().removePreference(iconBadgingPref);
- } else {
- // Listen to system notification badge settings while this UI is active.
- mIconBadgingObserver = new IconBadgingObserver(
- iconBadgingPref, resolver, getFragmentManager());
- mIconBadgingObserver.register(NOTIFICATION_BADGING, NOTIFICATION_ENABLED_LISTENERS);
- }
-
- Preference iconShapeOverride = findPreference(IconShapeOverride.KEY_PREFERENCE);
- if (iconShapeOverride != null) {
- if (IconShapeOverride.isSupported(getActivity())) {
- IconShapeOverride.handlePreferenceUi((ListPreference) iconShapeOverride);
- } else {
- getPreferenceScreen().removePreference(iconShapeOverride);
- }
- }
-
- // Setup allow rotation preference
- Preference rotationPref = findPreference(ALLOW_ROTATION_PREFERENCE_KEY);
- if (getResources().getBoolean(R.bool.allow_rotation)) {
- // Launcher supports rotation by default. No need to show this setting.
- getPreferenceScreen().removePreference(rotationPref);
- } else {
- // Initialize the UI once
- rotationPref.setDefaultValue(getAllowRotationDefaultValue());
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- Intent intent = getActivity().getIntent();
- mPreferenceKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
- if (isAdded() && !mPreferenceHighlighted && !TextUtils.isEmpty(mPreferenceKey)) {
- getView().postDelayed(this::highlightPreference, DELAY_HIGHLIGHT_DURATION_MILLIS);
- }
- }
-
- private void highlightPreference() {
- Preference pref = findPreference(mPreferenceKey);
- if (pref == null || getPreferenceScreen() == null) {
- return;
- }
- PreferenceScreen screen = getPreferenceScreen();
- if (Utilities.ATLEAST_OREO) {
- screen = selectPreferenceRecursive(pref, screen);
- }
- if (screen == null) {
- return;
- }
-
- View root = screen.getDialog() != null
- ? screen.getDialog().getWindow().getDecorView() : getView();
- ListView list = root.findViewById(android.R.id.list);
- if (list == null || list.getAdapter() == null) {
- return;
- }
- Adapter adapter = list.getAdapter();
-
- // Find the position
- int position = -1;
- for (int i = adapter.getCount() - 1; i >= 0; i--) {
- if (pref == adapter.getItem(i)) {
- position = i;
- break;
- }
- }
- new ListViewHighlighter(list, position);
- mPreferenceHighlighted = true;
- }
-
- @Override
- public void onDestroy() {
- if (mIconBadgingObserver != null) {
- mIconBadgingObserver.unregister();
- mIconBadgingObserver = null;
- }
- super.onDestroy();
- }
-
- @TargetApi(Build.VERSION_CODES.O)
- private PreferenceScreen selectPreferenceRecursive(
- Preference pref, PreferenceScreen topParent) {
- if (!(pref.getParent() instanceof PreferenceScreen)) {
- return null;
- }
-
- PreferenceScreen parent = (PreferenceScreen) pref.getParent();
- if (Objects.equals(parent.getKey(), topParent.getKey())) {
- return parent;
- } else if (selectPreferenceRecursive(parent, topParent) != null) {
- ((PreferenceScreen) parent.getParent())
- .onItemClick(null, null, parent.getOrder(), 0);
- return parent;
- } else {
- return null;
- }
- }
- }
-
- /**
- * Content observer which listens for system badging setting changes,
- * and updates the launcher badging setting subtext accordingly.
- */
- private static class IconBadgingObserver extends SettingsObserver.Secure {
-
- private final ButtonPreference mBadgingPref;
- private final ContentResolver mResolver;
- private final FragmentManager mFragmentManager;
-
- public IconBadgingObserver(ButtonPreference badgingPref, ContentResolver resolver,
- FragmentManager fragmentManager) {
- super(resolver);
- mBadgingPref = badgingPref;
- mResolver = resolver;
- mFragmentManager = fragmentManager;
- }
-
- @Override
- public void onSettingChanged(boolean enabled) {
- int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off;
-
- boolean serviceEnabled = true;
- if (enabled) {
- // Check if the listener is enabled or not.
- String enabledListeners =
- Settings.Secure.getString(mResolver, NOTIFICATION_ENABLED_LISTENERS);
- ComponentName myListener =
- new ComponentName(mBadgingPref.getContext(), NotificationListener.class);
- serviceEnabled = enabledListeners != null &&
- (enabledListeners.contains(myListener.flattenToString()) ||
- enabledListeners.contains(myListener.flattenToShortString()));
- if (!serviceEnabled) {
- summary = R.string.title_missing_notification_access;
- }
- }
- mBadgingPref.setWidgetFrameVisible(!serviceEnabled);
- mBadgingPref.setFragment(
- serviceEnabled ? null : NotificationAccessConfirmation.class.getName());
- mBadgingPref.setSummary(summary);
-
- }
- }
-
- public static class NotificationAccessConfirmation
- extends DialogFragment implements DialogInterface.OnClickListener {
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Context context = getActivity();
- String msg = context.getString(R.string.msg_missing_notification_access,
- context.getString(R.string.derived_app_name));
- return new AlertDialog.Builder(context)
- .setTitle(R.string.title_missing_notification_access)
- .setMessage(msg)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(R.string.title_change_settings, this)
- .create();
- }
-
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
- Bundle showFragmentArgs = new Bundle();
- showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString());
-
- Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString())
- .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
- getActivity().startActivity(intent);
- }
- }
-}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 5348349..90e195b 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -25,16 +25,22 @@
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
-
-import com.android.launcher3.R;
-import com.android.launcher3.anim.PropertySetter;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.systemui.plugins.AllAppsRow;
+import com.android.systemui.plugins.PluginListener;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
public class FloatingHeaderView extends LinearLayout implements
- ValueAnimator.AnimatorUpdateListener {
+ ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow> {
private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
@@ -64,6 +70,9 @@
private AllAppsRecyclerView mMainRV;
private AllAppsRecyclerView mWorkRV;
private AllAppsRecyclerView mCurrentRV;
+ protected final Map<AllAppsRow, View> mPluginRows;
+ // Contains just the values of the above map so we can iterate without extracting a new list.
+ protected final List<View> mPluginRowViews;
private ViewGroup mParent;
private boolean mHeaderCollapsed;
private int mSnappedScrolledY;
@@ -82,6 +91,8 @@
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
+ mPluginRows = new HashMap<>();
+ mPluginRowViews = new ArrayList<>();
}
@Override
@@ -90,6 +101,38 @@
mTabLayout = findViewById(R.id.tabs);
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ PluginManagerWrapper.INSTANCE.get(getContext()).addPluginListener(this,
+ AllAppsRow.class, true /* allowMultiple */);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(this);
+ }
+
+ @Override
+ public void onPluginConnected(AllAppsRow allAppsRowPlugin, Context context) {
+ mPluginRows.put(allAppsRowPlugin, null);
+ setupPluginRows();
+ allAppsRowPlugin.setOnHeightUpdatedListener(this::onPluginRowHeightUpdated);
+ }
+
+ protected void onPluginRowHeightUpdated() {
+ }
+
+ @Override
+ public void onPluginDisconnected(AllAppsRow plugin) {
+ View pluginRowView = mPluginRows.get(plugin);
+ removeView(pluginRowView);
+ mPluginRows.remove(plugin);
+ mPluginRowViews.remove(pluginRowView);
+ onPluginRowHeightUpdated();
+ }
+
public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
mTabsHidden = tabsHidden;
mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
@@ -97,9 +140,24 @@
mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView);
mParent = (ViewGroup) mMainRV.getParent();
setMainActive(mMainRVActive || mWorkRV == null);
+ setupPluginRows();
reset(false);
}
+ private void setupPluginRows() {
+ for (Map.Entry<AllAppsRow, View> rowPluginEntry : mPluginRows.entrySet()) {
+ if (rowPluginEntry.getValue() == null) {
+ View pluginRow = rowPluginEntry.getKey().setup(this);
+ addView(pluginRow, indexOfChild(mTabLayout));
+ rowPluginEntry.setValue(pluginRow);
+ mPluginRowViews.add(pluginRow);
+ }
+ }
+ for (View plugin : mPluginRowViews) {
+ plugin.setVisibility(mHeaderCollapsed ? GONE : VISIBLE);
+ }
+ }
+
private AllAppsRecyclerView setupRV(AllAppsRecyclerView old, AllAppsRecyclerView updated) {
if (old != updated && updated != null ) {
updated.addOnScrollListener(mOnScrollListener);
diff --git a/src/com/android/launcher3/graphics/IconShapeOverride.java b/src/com/android/launcher3/graphics/IconShapeOverride.java
index cadc6e3..b636c6d 100644
--- a/src/com/android/launcher3/graphics/IconShapeOverride.java
+++ b/src/com/android/launcher3/graphics/IconShapeOverride.java
@@ -26,9 +26,6 @@
import android.content.res.Resources;
import android.os.Build;
import android.os.SystemClock;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -42,6 +39,9 @@
import java.lang.reflect.Field;
import androidx.annotation.NonNull;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
/**
* Utility class to override shape of {@link android.graphics.drawable.AdaptiveIconDrawable}.
diff --git a/src/com/android/launcher3/icons/BaseIconFactory.java b/src/com/android/launcher3/icons/BaseIconFactory.java
new file mode 100644
index 0000000..db723b7
--- /dev/null
+++ b/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -0,0 +1,303 @@
+package com.android.launcher3.icons;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+
+import com.android.launcher3.R;
+
+import static android.graphics.Paint.DITHER_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR;
+
+/**
+ * This class will be moved to androidx library. There shouldn't be any dependency outside
+ * this package.
+ */
+public class BaseIconFactory {
+
+ private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
+ public static final boolean ATLEAST_OREO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+
+ private final Rect mOldBounds = new Rect();
+ private final Context mContext;
+ private final Canvas mCanvas;
+ private final PackageManager mPm;
+ private final ColorExtractor mColorExtractor;
+ private boolean mDisableColorExtractor;
+
+ private int mFillResIconDpi;
+ private int mIconBitmapSize;
+
+ private IconNormalizer mNormalizer;
+ private ShadowGenerator mShadowGenerator;
+
+ private Drawable mWrapperIcon;
+ private int mWrapperBackgroundColor;
+
+ protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) {
+ mContext = context.getApplicationContext();
+
+ mFillResIconDpi = fillResIconDpi;
+ mIconBitmapSize = iconBitmapSize;
+
+ mPm = mContext.getPackageManager();
+ mColorExtractor = new ColorExtractor();
+
+ mCanvas = new Canvas();
+ mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
+ }
+
+ protected void clear() {
+ mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
+ mDisableColorExtractor = false;
+ }
+
+ public ShadowGenerator getShadowGenerator() {
+ if (mShadowGenerator == null) {
+ mShadowGenerator = new ShadowGenerator(mContext);
+ }
+ return mShadowGenerator;
+ }
+
+ public IconNormalizer getNormalizer() {
+ if (mNormalizer == null) {
+ mNormalizer = new IconNormalizer(mContext);
+ }
+ return mNormalizer;
+ }
+
+ public BitmapInfo createIconBitmap(Intent.ShortcutIconResource iconRes) {
+ try {
+ Resources resources = mPm.getResourcesForApplication(iconRes.packageName);
+ if (resources != null) {
+ final int id = resources.getIdentifier(iconRes.resourceName, null, null);
+ // do not stamp old legacy shortcuts as the app may have already forgotten about it
+ return createBadgedIconBitmap(
+ resources.getDrawableForDensity(id, mFillResIconDpi),
+ Process.myUserHandle() /* only available on primary user */,
+ false /* do not apply legacy treatment */);
+ }
+ } catch (Exception e) {
+ // Icon not found.
+ }
+ return null;
+ }
+
+ public BitmapInfo createIconBitmap(Bitmap icon) {
+ if (mIconBitmapSize == icon.getWidth() && mIconBitmapSize == icon.getHeight()) {
+ return BitmapInfo.fromBitmap(icon);
+ }
+ return BitmapInfo.fromBitmap(
+ createIconBitmap(new BitmapDrawable(mContext.getResources(), icon), 1f));
+ }
+
+ public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+ boolean shrinkNonAdaptiveIcons) {
+ return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, false, null);
+ }
+
+ public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+ boolean shrinkNonAdaptiveIcons, boolean isInstantApp) {
+ return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, isInstantApp, null);
+ }
+
+ /**
+ * Creates bitmap using the source drawable and various parameters.
+ * The bitmap is visually normalized with other icons and has enough spacing to add shadow.
+ *
+ * @param icon source of the icon
+ * @param user info can be used for a badge
+ * @param shrinkNonAdaptiveIcons {@code true} if non adaptive icons should be treated
+ * @param isInstantApp info can be used for a badge
+ * @param scale returns the scale result from normalization
+ * @return a bitmap suitable for disaplaying as an icon at various system UIs.
+ */
+ public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+ boolean shrinkNonAdaptiveIcons, boolean isInstantApp, float[] scale) {
+ if (scale == null) {
+ scale = new float[1];
+ }
+ icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, null, scale);
+ Bitmap bitmap = createIconBitmap(icon, scale[0]);
+ if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
+ mCanvas.setBitmap(bitmap);
+ getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas);
+ mCanvas.setBitmap(null);
+ }
+
+ final Bitmap result;
+ if (user != null && !Process.myUserHandle().equals(user)) {
+ BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
+ Drawable badged = mPm.getUserBadgedIcon(drawable, user);
+ if (badged instanceof BitmapDrawable) {
+ result = ((BitmapDrawable) badged).getBitmap();
+ } else {
+ result = createIconBitmap(badged, 1f);
+ }
+ } else if (isInstantApp) {
+ badgeWithDrawable(bitmap, mContext.getDrawable(R.drawable.ic_instant_app_badge));
+ result = bitmap;
+ } else {
+ result = bitmap;
+ }
+ return BitmapInfo.fromBitmap(result, mDisableColorExtractor ? null : mColorExtractor);
+ }
+
+ public Bitmap createScaledBitmapWithoutShadow(Drawable icon, boolean shrinkNonAdaptiveIcons) {
+ RectF iconBounds = new RectF();
+ float[] scale = new float[1];
+ icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, iconBounds, scale);
+ return createIconBitmap(icon,
+ Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds)));
+ }
+
+ /**
+ * Sets the background color used for wrapped adaptive icon
+ */
+ public void setWrapperBackgroundColor(int color) {
+ mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color;
+ }
+
+ /**
+ * Disables the dominant color extraction for all icons loaded.
+ */
+ public void disableColorExtraction() {
+ mDisableColorExtractor = true;
+ }
+
+ private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, boolean shrinkNonAdaptiveIcons,
+ RectF outIconBounds, float[] outScale) {
+ float scale = 1f;
+
+ if (shrinkNonAdaptiveIcons) {
+ boolean[] outShape = new boolean[1];
+ if (mWrapperIcon == null) {
+ mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper)
+ .mutate();
+ }
+ AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
+ dr.setBounds(0, 0, 1, 1);
+ scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
+ if (ATLEAST_OREO && !outShape[0] && !(icon instanceof AdaptiveIconDrawable)) {
+ FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
+ fsd.setDrawable(icon);
+ fsd.setScale(scale);
+ icon = dr;
+ scale = getNormalizer().getScale(icon, outIconBounds, null, null);
+
+ ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
+ }
+ } else {
+ scale = getNormalizer().getScale(icon, outIconBounds, null, null);
+ }
+
+ outScale[0] = scale;
+ return icon;
+ }
+
+ /**
+ * Adds the {@param badge} on top of {@param target} using the badge dimensions.
+ */
+ public void badgeWithDrawable(Bitmap target, Drawable badge) {
+ mCanvas.setBitmap(target);
+ badgeWithDrawable(mCanvas, badge);
+ mCanvas.setBitmap(null);
+ }
+
+ /**
+ * Adds the {@param badge} on top of {@param target} using the badge dimensions.
+ */
+ public void badgeWithDrawable(Canvas target, Drawable badge) {
+ int badgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
+ badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize,
+ mIconBitmapSize, mIconBitmapSize);
+ badge.draw(target);
+ }
+
+ /**
+ * @param scale the scale to apply before drawing {@param icon} on the canvas
+ */
+ private Bitmap createIconBitmap(Drawable icon, float scale) {
+ Bitmap bitmap = Bitmap.createBitmap(mIconBitmapSize, mIconBitmapSize,
+ Bitmap.Config.ARGB_8888);
+ mCanvas.setBitmap(bitmap);
+ mOldBounds.set(icon.getBounds());
+
+ if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
+ int offset = Math.max((int) Math.ceil(BLUR_FACTOR * mIconBitmapSize),
+ Math.round(mIconBitmapSize * (1 - scale) / 2 ));
+ icon.setBounds(offset, offset, mIconBitmapSize - offset, mIconBitmapSize - offset);
+ icon.draw(mCanvas);
+ } else {
+ if (icon instanceof BitmapDrawable) {
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
+ Bitmap b = bitmapDrawable.getBitmap();
+ if (bitmap != null && b.getDensity() == Bitmap.DENSITY_NONE) {
+ bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics());
+ }
+ }
+ int width = mIconBitmapSize;
+ int height = mIconBitmapSize;
+
+ int intrinsicWidth = icon.getIntrinsicWidth();
+ int intrinsicHeight = icon.getIntrinsicHeight();
+ if (intrinsicWidth > 0 && intrinsicHeight > 0) {
+ // Scale the icon proportionally to the icon dimensions
+ final float ratio = (float) intrinsicWidth / intrinsicHeight;
+ if (intrinsicWidth > intrinsicHeight) {
+ height = (int) (width / ratio);
+ } else if (intrinsicHeight > intrinsicWidth) {
+ width = (int) (height * ratio);
+ }
+ }
+ final int left = (mIconBitmapSize - width) / 2;
+ final int top = (mIconBitmapSize - height) / 2;
+ icon.setBounds(left, top, left + width, top + height);
+ mCanvas.save();
+ mCanvas.scale(scale, scale, mIconBitmapSize / 2, mIconBitmapSize / 2);
+ icon.draw(mCanvas);
+ mCanvas.restore();
+
+ }
+ icon.setBounds(mOldBounds);
+ mCanvas.setBitmap(null);
+ return bitmap;
+ }
+
+ /**
+ * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size.
+ * This allows the badging to be done based on the action bitmap size rather than
+ * the scaled bitmap size.
+ */
+ private static class FixedSizeBitmapDrawable extends BitmapDrawable {
+
+ public FixedSizeBitmapDrawable(Bitmap bitmap) {
+ super(null, bitmap);
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return getBitmap().getWidth();
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return getBitmap().getWidth();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 244654b..69614a3 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -16,29 +16,11 @@
package com.android.launcher3.icons;
-import static android.graphics.Paint.DITHER_FLAG;
-import static android.graphics.Paint.FILTER_BITMAP_FLAG;
-
-import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR;
-
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.Intent.ShortcutIconResource;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.PaintDrawable;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
@@ -49,7 +31,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -60,14 +41,14 @@
import androidx.annotation.Nullable;
/**
- * Helper methods for generating various launcher icons
+ * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
+ * that are threadsafe.
*/
-public class LauncherIcons implements AutoCloseable {
+public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
- private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
-
- public static final Object sPoolSync = new Object();
+ private static final Object sPoolSync = new Object();
private static LauncherIcons sPool;
+ private LauncherIcons next;
/**
* Return a new Message instance from the global pool. Allows us to
@@ -82,7 +63,8 @@
return m;
}
}
- return new LauncherIcons(context);
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize);
}
/**
@@ -91,8 +73,7 @@
public void recycle() {
synchronized (sPoolSync) {
// Clear any temporary state variables
- mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
- mDisableColorExtractor = false;
+ clear();
next = sPool;
sPool = this;
@@ -104,259 +85,41 @@
recycle();
}
- private final Rect mOldBounds = new Rect();
private final Context mContext;
- private final Canvas mCanvas;
- private final PackageManager mPm;
- private final ColorExtractor mColorExtractor;
- private boolean mDisableColorExtractor;
-
private final int mFillResIconDpi;
private final int mIconBitmapSize;
- private IconNormalizer mNormalizer;
- private ShadowGenerator mShadowGenerator;
-
- private Drawable mWrapperIcon;
- private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
-
- // sometimes we store linked lists of these things
- private LauncherIcons next;
-
- private LauncherIcons(Context context) {
+ private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize) {
+ super(context, fillResIconDpi, iconBitmapSize);
mContext = context.getApplicationContext();
- mPm = mContext.getPackageManager();
- mColorExtractor = new ColorExtractor();
-
- InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
- mFillResIconDpi = idp.fillResIconDpi;
- mIconBitmapSize = idp.iconBitmapSize;
-
- mCanvas = new Canvas();
- mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
+ mFillResIconDpi = fillResIconDpi;
+ mIconBitmapSize = iconBitmapSize;
}
- public ShadowGenerator getShadowGenerator() {
- if (mShadowGenerator == null) {
- mShadowGenerator = new ShadowGenerator(mContext);
- }
- return mShadowGenerator;
+ public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+ int iconAppTargetSdk) {
+ return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false);
}
- public IconNormalizer getNormalizer() {
- if (mNormalizer == null) {
- mNormalizer = new IconNormalizer(mContext);
- }
- return mNormalizer;
- }
-
- /**
- * Returns a bitmap suitable for the all apps view. If the package or the resource do not
- * exist, it returns null.
- */
- public BitmapInfo createIconBitmap(ShortcutIconResource iconRes) {
- try {
- Resources resources = mPm.getResourcesForApplication(iconRes.packageName);
- if (resources != null) {
- final int id = resources.getIdentifier(iconRes.resourceName, null, null);
- // do not stamp old legacy shortcuts as the app may have already forgotten about it
- return createBadgedIconBitmap(
- resources.getDrawableForDensity(id, mFillResIconDpi),
- Process.myUserHandle() /* only available on primary user */,
- 0 /* do not apply legacy treatment */);
- }
- } catch (Exception e) {
- // Icon not found.
- }
- return null;
- }
-
- /**
- * Returns a bitmap which is of the appropriate size to be displayed as an icon
- */
- public BitmapInfo createIconBitmap(Bitmap icon) {
- if (mIconBitmapSize == icon.getWidth() && mIconBitmapSize == icon.getHeight()) {
- return BitmapInfo.fromBitmap(icon);
- }
- return BitmapInfo.fromBitmap(
- createIconBitmap(new BitmapDrawable(mContext.getResources(), icon), 1f));
- }
-
- /**
- * Returns a bitmap suitable for displaying as an icon at various launcher UIs like all apps
- * view or workspace. The icon is badged for {@param user}.
- * The bitmap is also visually normalized with other icons.
- */
- public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk) {
- return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false, null);
- }
-
- public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk,
- boolean isInstantApp) {
+ public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+ int iconAppTargetSdk, boolean isInstantApp) {
return createBadgedIconBitmap(icon, user, iconAppTargetSdk, isInstantApp, null);
}
- /**
- * Returns a bitmap suitable for displaying as an icon at various launcher UIs like all apps
- * view or workspace. The icon is badged for {@param user}.
- * The bitmap is also visually normalized with other icons.
- */
- public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk,
- boolean isInstantApp, float[] scale) {
- if (scale == null) {
- scale = new float[1];
- }
- icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, null, scale);
- Bitmap bitmap = createIconBitmap(icon, scale[0]);
- if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
- mCanvas.setBitmap(bitmap);
- getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas);
- mCanvas.setBitmap(null);
- }
-
- final Bitmap result;
- if (user != null && !Process.myUserHandle().equals(user)) {
- BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
- Drawable badged = mPm.getUserBadgedIcon(drawable, user);
- if (badged instanceof BitmapDrawable) {
- result = ((BitmapDrawable) badged).getBitmap();
- } else {
- result = createIconBitmap(badged, 1f);
- }
- } else if (isInstantApp) {
- badgeWithDrawable(bitmap, mContext.getDrawable(R.drawable.ic_instant_app_badge));
- result = bitmap;
- } else {
- result = bitmap;
- }
- return BitmapInfo.fromBitmap(result, mDisableColorExtractor ? null : mColorExtractor);
+ public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+ int iconAppTargetSdk, boolean isInstantApp, float[] scale) {
+ boolean shrinkNonAdaptiveIcons = Utilities.ATLEAST_P ||
+ (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O);
+ return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, isInstantApp, scale);
}
- /**
- * Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually
- * normalized with other icons and has enough spacing to add shadow.
- */
public Bitmap createScaledBitmapWithoutShadow(Drawable icon, int iconAppTargetSdk) {
- RectF iconBounds = new RectF();
- float[] scale = new float[1];
- icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, iconBounds, scale);
- return createIconBitmap(icon,
- Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds)));
+ boolean shrinkNonAdaptiveIcons = Utilities.ATLEAST_P ||
+ (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O);
+ return createScaledBitmapWithoutShadow(icon, shrinkNonAdaptiveIcons);
}
- /**
- * Sets the background color used for wrapped adaptive icon
- */
- public void setWrapperBackgroundColor(int color) {
- mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color;
- }
-
- /**
- * Disables the dominant color extraction for all icons loaded through this session (until
- * this instance is recycled).
- */
- public void disableColorExtraction() {
- mDisableColorExtractor = true;
- }
-
- private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, int iconAppTargetSdk,
- RectF outIconBounds, float[] outScale) {
- float scale = 1f;
- if ((Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) ||
- Utilities.ATLEAST_P) {
- boolean[] outShape = new boolean[1];
- if (mWrapperIcon == null) {
- mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper)
- .mutate();
- }
- AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
- dr.setBounds(0, 0, 1, 1);
- scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
- if (Utilities.ATLEAST_OREO && !outShape[0] && !(icon instanceof AdaptiveIconDrawable)) {
- FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
- fsd.setDrawable(icon);
- fsd.setScale(scale);
- icon = dr;
- scale = getNormalizer().getScale(icon, outIconBounds, null, null);
-
- ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
- }
- } else {
- scale = getNormalizer().getScale(icon, outIconBounds, null, null);
- }
-
- outScale[0] = scale;
- return icon;
- }
-
- /**
- * Adds the {@param badge} on top of {@param target} using the badge dimensions.
- */
- public void badgeWithDrawable(Bitmap target, Drawable badge) {
- mCanvas.setBitmap(target);
- badgeWithDrawable(mCanvas, badge);
- mCanvas.setBitmap(null);
- }
-
- /**
- * Adds the {@param badge} on top of {@param target} using the badge dimensions.
- */
- private void badgeWithDrawable(Canvas target, Drawable badge) {
- int badgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
- badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize,
- mIconBitmapSize, mIconBitmapSize);
- badge.draw(target);
- }
-
- /**
- * @param scale the scale to apply before drawing {@param icon} on the canvas
- */
- private Bitmap createIconBitmap(Drawable icon, float scale) {
- Bitmap bitmap = Bitmap.createBitmap(mIconBitmapSize, mIconBitmapSize,
- Bitmap.Config.ARGB_8888);
- mCanvas.setBitmap(bitmap);
- mOldBounds.set(icon.getBounds());
-
- if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
- int offset = Math.max((int) Math.ceil(BLUR_FACTOR * mIconBitmapSize),
- Math.round(mIconBitmapSize * (1 - scale) / 2 ));
- icon.setBounds(offset, offset, mIconBitmapSize - offset, mIconBitmapSize - offset);
- icon.draw(mCanvas);
- } else {
- if (icon instanceof BitmapDrawable) {
- BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
- Bitmap b = bitmapDrawable.getBitmap();
- if (bitmap != null && b.getDensity() == Bitmap.DENSITY_NONE) {
- bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics());
- }
- }
- int width = mIconBitmapSize;
- int height = mIconBitmapSize;
-
- int intrinsicWidth = icon.getIntrinsicWidth();
- int intrinsicHeight = icon.getIntrinsicHeight();
- if (intrinsicWidth > 0 && intrinsicHeight > 0) {
- // Scale the icon proportionally to the icon dimensions
- final float ratio = (float) intrinsicWidth / intrinsicHeight;
- if (intrinsicWidth > intrinsicHeight) {
- height = (int) (width / ratio);
- } else if (intrinsicHeight > intrinsicWidth) {
- width = (int) (height * ratio);
- }
- }
- final int left = (mIconBitmapSize - width) / 2;
- final int top = (mIconBitmapSize - height) / 2;
- icon.setBounds(left, top, left + width, top + height);
- mCanvas.save();
- mCanvas.scale(scale, scale, mIconBitmapSize / 2, mIconBitmapSize / 2);
- icon.draw(mCanvas);
- mCanvas.restore();
-
- }
- icon.setBounds(mOldBounds);
- mCanvas.setBitmap(null);
- return bitmap;
- }
+ // below methods should also migrate to BaseIconFactory
public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo) {
return createShortcutIcon(shortcutInfo, true /* badged */);
@@ -424,26 +187,4 @@
return pkgInfo;
}
}
-
- /**
- * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size.
- * This allows the badging to be done based on the action bitmap size rather than
- * the scaled bitmap size.
- */
- private static class FixedSizeBitmapDrawable extends BitmapDrawable {
-
- public FixedSizeBitmapDrawable(Bitmap bitmap) {
- super(null, bitmap);
- }
-
- @Override
- public int getIntrinsicHeight() {
- return getBitmap().getWidth();
- }
-
- @Override
- public int getIntrinsicWidth() {
- return getBitmap().getWidth();
- }
- }
}
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 68cf7de..f27b728 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -16,7 +16,7 @@
package com.android.launcher3.notification;
-import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
+import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
import android.annotation.TargetApi;
import android.app.Notification;
@@ -28,14 +28,13 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.SettingsObserver;
+import com.android.launcher3.util.SecureSettingsObserver;
import java.util.ArrayList;
import java.util.Arrays;
@@ -43,7 +42,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import androidx.annotation.Nullable;
@@ -79,7 +77,7 @@
/** The last notification key that was dismissed from launcher UI */
private String mLastKeyDismissedByLauncher;
- private SettingsObserver mNotificationBadgingObserver;
+ private SecureSettingsObserver mNotificationBadgingObserver;
private final Handler.Callback mWorkerCallback = new Handler.Callback() {
@Override
@@ -196,19 +194,20 @@
super.onListenerConnected();
sIsConnected = true;
- mNotificationBadgingObserver = new SettingsObserver.Secure(getContentResolver()) {
- @Override
- public void onSettingChanged(boolean isNotificationBadgingEnabled) {
- if (!isNotificationBadgingEnabled && sIsConnected) {
- requestUnbind();
- }
- }
- };
- mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
+ mNotificationBadgingObserver =
+ newNotificationSettingsObserver(this, this::onNotificationBadgingChanged);
+ mNotificationBadgingObserver.register();
+ mNotificationBadgingObserver.dispatchOnChange();
onNotificationFullRefresh();
}
+ private void onNotificationBadgingChanged(boolean isNotificationBadgingEnabled) {
+ if (!isNotificationBadgingEnabled && sIsConnected) {
+ requestUnbind();
+ }
+ }
+
private void onNotificationFullRefresh() {
mWorkerHandler.obtainMessage(MSG_NOTIFICATION_FULL_REFRESH).sendToTarget();
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 6877cc4..b9e6a98 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -391,13 +391,10 @@
if (view instanceof DeepShortcutView) {
// Expanded system shortcut, with both icon and text shown on white background.
final DeepShortcutView shortcutView = (DeepShortcutView) view;
- shortcutView.getIconView().setBackgroundResource(info.iconResId);
- shortcutView.getBubbleText().setText(info.labelResId);
+ info.setIconAndLabelFor(shortcutView.getIconView(), shortcutView.getBubbleText());
} else if (view instanceof ImageView) {
// Only the system shortcut icon shows on a gray background header.
- final ImageView shortcutIcon = (ImageView) view;
- shortcutIcon.setImageResource(info.iconResId);
- shortcutIcon.setContentDescription(getContext().getText(info.labelResId));
+ info.setIconAndContentDescriptionFor((ImageView) view);
}
view.setTag(info);
view.setOnClickListener(info.getOnClickListener(mLauncher,
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 693e532..b80ba8a 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -3,10 +3,15 @@
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.ImageView;
+import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
@@ -23,18 +28,82 @@
import java.util.List;
/**
- * Represents a system shortcut for a given app. The shortcut should have a static label and
- * icon, and an onClickListener that depends on the item that the shortcut services.
+ * Represents a system shortcut for a given app. The shortcut should have a label and icon, and an
+ * onClickListener that depends on the item that the shortcut services.
*
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
*/
public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo {
- public final int iconResId;
- public final int labelResId;
+ private final int mIconResId;
+ private final int mLabelResId;
+ private final Drawable mIcon;
+ private final CharSequence mLabel;
+ private final CharSequence mContentDescription;
+ private final int mAccessibilityActionId;
public SystemShortcut(int iconResId, int labelResId) {
- this.iconResId = iconResId;
- this.labelResId = labelResId;
+ mIconResId = iconResId;
+ mLabelResId = labelResId;
+ mAccessibilityActionId = labelResId;
+ mIcon = null;
+ mLabel = null;
+ mContentDescription = null;
+ }
+
+ public SystemShortcut(Drawable icon, CharSequence label, CharSequence contentDescription,
+ int accessibilityActionId) {
+ mIcon = icon;
+ mLabel = label;
+ mContentDescription = contentDescription;
+ mAccessibilityActionId = accessibilityActionId;
+ mIconResId = 0;
+ mLabelResId = 0;
+ }
+
+ public SystemShortcut(SystemShortcut other) {
+ mIconResId = other.mIconResId;
+ mLabelResId = other.mLabelResId;
+ mIcon = other.mIcon;
+ mLabel = other.mLabel;
+ mContentDescription = other.mContentDescription;
+ mAccessibilityActionId = other.mAccessibilityActionId;
+ }
+
+ public void setIconAndLabelFor(View iconView, TextView labelView) {
+ if (mIcon != null) {
+ iconView.setBackground(mIcon);
+ } else {
+ iconView.setBackgroundResource(mIconResId);
+ }
+
+ if (mLabel != null) {
+ labelView.setText(mLabel);
+ } else {
+ labelView.setText(mLabelResId);
+ }
+ }
+
+ public void setIconAndContentDescriptionFor(ImageView view) {
+ if (mIcon != null) {
+ view.setImageDrawable(mIcon);
+ } else {
+ view.setImageResource(mIconResId);
+ }
+
+ view.setContentDescription(getContentDescription(view.getContext()));
+ }
+
+ private CharSequence getContentDescription(Context context) {
+ return mContentDescription != null ? mContentDescription : context.getText(mLabelResId);
+ }
+
+ public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
+ return new AccessibilityNodeInfo.AccessibilityAction(mAccessibilityActionId,
+ getContentDescription(context));
+ }
+
+ public boolean hasHandlerForAction(int action) {
+ return mAccessibilityActionId == action;
}
public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo);
diff --git a/src/com/android/launcher3/settings/IconBadgingPreference.java b/src/com/android/launcher3/settings/IconBadgingPreference.java
new file mode 100644
index 0000000..7c97b38
--- /dev/null
+++ b/src/com/android/launcher3/settings/IconBadgingPreference.java
@@ -0,0 +1,138 @@
+/*
+ * 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.settings;
+
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGS;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.util.SecureSettingsObserver;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * A {@link Preference} for indicating icon badging status.
+ * Also has utility methods for updating UI based on badging status changes.
+ */
+public class IconBadgingPreference extends Preference
+ implements SecureSettingsObserver.OnChangeListener {
+
+ private boolean mWidgetFrameVisible = false;
+
+ /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
+ private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
+
+ public IconBadgingPreference(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public IconBadgingPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public IconBadgingPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public IconBadgingPreference(Context context) {
+ super(context);
+ }
+
+ private void setWidgetFrameVisible(boolean isVisible) {
+ if (mWidgetFrameVisible != isVisible) {
+ mWidgetFrameVisible = isVisible;
+ notifyChanged();
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ View widgetFrame = holder.findViewById(android.R.id.widget_frame);
+ if (widgetFrame != null) {
+ widgetFrame.setVisibility(mWidgetFrameVisible ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ @Override
+ public void onSettingsChanged(boolean enabled) {
+ int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off;
+
+ boolean serviceEnabled = true;
+ if (enabled) {
+ // Check if the listener is enabled or not.
+ String enabledListeners = Settings.Secure.getString(
+ getContext().getContentResolver(), NOTIFICATION_ENABLED_LISTENERS);
+ ComponentName myListener =
+ new ComponentName(getContext(), NotificationListener.class);
+ serviceEnabled = enabledListeners != null &&
+ (enabledListeners.contains(myListener.flattenToString()) ||
+ enabledListeners.contains(myListener.flattenToShortString()));
+ if (!serviceEnabled) {
+ summary = R.string.title_missing_notification_access;
+ }
+ }
+ setWidgetFrameVisible(!serviceEnabled);
+ setFragment(serviceEnabled ? null : NotificationAccessConfirmation.class.getName());
+ setSummary(summary);
+ }
+
+ public static class NotificationAccessConfirmation
+ extends DialogFragment implements DialogInterface.OnClickListener {
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ String msg = context.getString(R.string.msg_missing_notification_access,
+ context.getString(R.string.derived_app_name));
+ return new AlertDialog.Builder(context)
+ .setTitle(R.string.title_missing_notification_access)
+ .setMessage(msg)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(R.string.title_change_settings, this)
+ .create();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
+ Bundle showFragmentArgs = new Bundle();
+ showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString());
+
+ Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString())
+ .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
+ getActivity().startActivity(intent);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/settings/PreferenceHighlighter.java b/src/com/android/launcher3/settings/PreferenceHighlighter.java
new file mode 100644
index 0000000..4ed4cf1
--- /dev/null
+++ b/src/com/android/launcher3/settings/PreferenceHighlighter.java
@@ -0,0 +1,127 @@
+/*
+ * 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.settings;
+
+import static androidx.core.graphics.ColorUtils.setAlphaComponent;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.Property;
+import android.view.View;
+
+import com.android.launcher3.util.Themes;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
+import androidx.recyclerview.widget.RecyclerView.State;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Utility class for highlighting a preference
+ */
+public class PreferenceHighlighter extends ItemDecoration implements Runnable {
+
+ private static final Property<PreferenceHighlighter, Integer> HIGHLIGHT_COLOR =
+ new Property<PreferenceHighlighter, Integer>(Integer.TYPE, "highlightColor") {
+
+ @Override
+ public Integer get(PreferenceHighlighter highlighter) {
+ return highlighter.mHighlightColor;
+ }
+
+ @Override
+ public void set(PreferenceHighlighter highlighter, Integer value) {
+ highlighter.mHighlightColor = value;
+ highlighter.mRv.invalidateItemDecorations();
+ }
+ };
+
+ private static final long HIGHLIGHT_DURATION = 15000L;
+ private static final long HIGHLIGHT_FADE_OUT_DURATION = 500L;
+ private static final long HIGHLIGHT_FADE_IN_DURATION = 200L;
+ private static final int END_COLOR = setAlphaComponent(Color.WHITE, 0);
+
+ private final Paint mPaint = new Paint();
+ private final RecyclerView mRv;
+ private final int mIndex;
+
+ private boolean mHighLightStarted = false;
+ private int mHighlightColor = END_COLOR;
+
+
+ public PreferenceHighlighter(RecyclerView rv, int index) {
+ mRv = rv;
+ mIndex = index;
+ }
+
+ @Override
+ public void run() {
+ mRv.addItemDecoration(this);
+ mRv.smoothScrollToPosition(mIndex);
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, State state) {
+ ViewHolder holder = parent.findViewHolderForAdapterPosition(mIndex);
+ if (holder == null) {
+ return;
+ }
+ if (!mHighLightStarted && state.getRemainingScrollVertical() != 0) {
+ // Wait until scrolling stopped
+ return;
+ }
+
+ if (!mHighLightStarted) {
+ // Start highlight
+ int colorTo = setAlphaComponent(Themes.getColorAccent(mRv.getContext()), 66);
+ ObjectAnimator anim = ObjectAnimator.ofArgb(this, HIGHLIGHT_COLOR, END_COLOR, colorTo);
+ anim.setDuration(HIGHLIGHT_FADE_IN_DURATION);
+ anim.setRepeatMode(ValueAnimator.REVERSE);
+ anim.setRepeatCount(4);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ removeHighlight();
+ }
+ });
+ anim.start();
+ mHighLightStarted = true;
+ }
+
+ View view = holder.itemView;
+ mPaint.setColor(mHighlightColor);
+ c.drawRect(0, view.getY(), parent.getWidth(), view.getY() + view.getHeight(), mPaint);
+ }
+
+ private void removeHighlight() {
+ ObjectAnimator anim = ObjectAnimator.ofArgb(
+ this, HIGHLIGHT_COLOR, mHighlightColor, END_COLOR);
+ anim.setDuration(HIGHLIGHT_FADE_OUT_DURATION);
+ anim.setStartDelay(HIGHLIGHT_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRv.removeItemDecoration(PreferenceHighlighter.this);
+ }
+ });
+ anim.start();
+ }
+}
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
new file mode 100644
index 0000000..66420d0
--- /dev/null
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2015 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.settings;
+
+import static com.android.launcher3.SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY;
+import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
+import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
+import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.IconShapeOverride;
+import com.android.launcher3.util.SecureSettingsObserver;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceFragment.OnPreferenceStartFragmentCallback;
+import androidx.preference.PreferenceFragment.OnPreferenceStartScreenCallback;
+import androidx.preference.PreferenceGroup.PreferencePositionCallback;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Settings activity for Launcher. Currently implements the following setting: Allow rotation
+ */
+public class SettingsActivity extends Activity
+ implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback {
+
+ private static final String FLAGS_PREFERENCE_KEY = "flag_toggler";
+
+ private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
+ /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
+ private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
+
+ public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+ public static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
+ private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
+ public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ Bundle args = new Bundle();
+ String prefKey = getIntent().getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
+ if (!TextUtils.isEmpty(prefKey)) {
+ args.putString(EXTRA_FRAGMENT_ARG_KEY, prefKey);
+ }
+
+ Fragment f = Fragment.instantiate(
+ this, getString(R.string.settings_fragment_name), args);
+ // Display the fragment as the main content.
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, f)
+ .commit();
+ }
+ }
+
+ private boolean startFragment(String fragment, Bundle args, String key) {
+ if (Utilities.ATLEAST_P && getFragmentManager().isStateSaved()) {
+ // Sometimes onClick can come after onPause because of being posted on the handler.
+ // Skip starting new fragments in that case.
+ return false;
+ }
+ Fragment f = Fragment.instantiate(this, fragment, args);
+ if (f instanceof DialogFragment) {
+ ((DialogFragment) f).show(getFragmentManager(), key);
+ } else {
+ getFragmentManager()
+ .beginTransaction()
+ .replace(android.R.id.content, f)
+ .addToBackStack(key)
+ .commit();
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceStartFragment(
+ PreferenceFragment preferenceFragment, Preference pref) {
+ return startFragment(pref.getFragment(), pref.getExtras(), pref.getKey());
+ }
+
+ @Override
+ public boolean onPreferenceStartScreen(PreferenceFragment caller, PreferenceScreen pref) {
+ Bundle args = new Bundle();
+ args.putString(PreferenceFragment.ARG_PREFERENCE_ROOT, pref.getKey());
+ return startFragment(getString(R.string.settings_fragment_name), args, pref.getKey());
+ }
+
+ /**
+ * This fragment shows the launcher preferences.
+ */
+ public static class LauncherSettingsFragment extends PreferenceFragment {
+
+ private SecureSettingsObserver mIconBadgingObserver;
+
+ private String mHighLightKey;
+ private boolean mPreferenceHighlighted = false;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ final Bundle args = getArguments();
+ mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_ARG_KEY);
+ if (rootKey == null && !TextUtils.isEmpty(mHighLightKey)) {
+ rootKey = getParentKeyForPref(mHighLightKey);
+ }
+
+ if (savedInstanceState != null) {
+ mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
+ }
+
+ getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
+ setPreferencesFromResource(R.xml.launcher_preferences, rootKey);
+
+ PreferenceScreen screen = getPreferenceScreen();
+ for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
+ Preference preference = screen.getPreference(i);
+ if (!initPreference(preference)) {
+ screen.removePreference(preference);
+ }
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+ }
+
+ protected String getParentKeyForPref(String key) {
+ return null;
+ }
+
+ /**
+ * Initializes a preference. This is called for every preference. Returning false here
+ * will remove that preference from the list.
+ */
+ protected boolean initPreference(Preference preference) {
+ switch (preference.getKey()) {
+ case ICON_BADGING_PREFERENCE_KEY:
+ if (!Utilities.ATLEAST_OREO ||
+ !getResources().getBoolean(R.bool.notification_badging_enabled)) {
+ return false;
+ }
+
+ // Listen to system notification badge settings while this UI is active.
+ mIconBadgingObserver = newNotificationSettingsObserver(
+ getActivity(), (IconBadgingPreference) preference);
+ mIconBadgingObserver.register();
+ // Also listen if notification permission changes
+ mIconBadgingObserver.getResolver().registerContentObserver(
+ Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS), false,
+ mIconBadgingObserver);
+ mIconBadgingObserver.dispatchOnChange();
+ return true;
+
+ case ADD_ICON_PREFERENCE_KEY:
+ return Utilities.ATLEAST_OREO;
+
+ case IconShapeOverride.KEY_PREFERENCE:
+ if (!IconShapeOverride.isSupported(getActivity())) {
+ return false;
+ }
+ IconShapeOverride.handlePreferenceUi((ListPreference) preference);
+ return true;
+
+ case ALLOW_ROTATION_PREFERENCE_KEY:
+ if (getResources().getBoolean(R.bool.allow_rotation)) {
+ // Launcher supports rotation by default. No need to show this setting.
+ return false;
+ }
+ // Initialize the UI once
+ preference.setDefaultValue(getAllowRotationDefaultValue());
+ return true;
+
+ case FLAGS_PREFERENCE_KEY:
+ // Only show flag toggler UI if this build variant implements that.
+ return FeatureFlags.showFlagTogglerUi();
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (isAdded() && !mPreferenceHighlighted) {
+ PreferenceHighlighter highlighter = createHighlighter();
+ if (highlighter != null) {
+ getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ mPreferenceHighlighted = true;
+ }
+ }
+ }
+
+ private PreferenceHighlighter createHighlighter() {
+ if (TextUtils.isEmpty(mHighLightKey)) {
+ return null;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ return null;
+ }
+
+ RecyclerView list = getListView();
+ PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter();
+ int position = callback.getPreferenceAdapterPosition(mHighLightKey);
+ return position >= 0 ? new PreferenceHighlighter(list, position) : null;
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mIconBadgingObserver != null) {
+ mIconBadgingObserver.unregister();
+ mIconBadgingObserver = null;
+ }
+ super.onDestroy();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/ListViewHighlighter.java b/src/com/android/launcher3/util/ListViewHighlighter.java
deleted file mode 100644
index c9fe228..0000000
--- a/src/com/android/launcher3/util/ListViewHighlighter.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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.animation.ArgbEvaluator;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.widget.AbsListView;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.AbsListView.RecyclerListener;
-import android.widget.ListView;
-
-import com.android.launcher3.R;
-
-import androidx.core.graphics.ColorUtils;
-
-/**
- * Utility class to scroll and highlight a list view item
- */
-public class ListViewHighlighter implements OnScrollListener, RecyclerListener,
- OnLayoutChangeListener {
-
- private final ListView mListView;
- private int mPosHighlight;
-
- private boolean mColorAnimated = false;
-
- public ListViewHighlighter(ListView listView, int posHighlight) {
- mListView = listView;
- mPosHighlight = posHighlight;
- mListView.setOnScrollListener(this);
- mListView.setRecyclerListener(this);
- mListView.addOnLayoutChangeListener(this);
-
- mListView.post(this::tryHighlight);
- }
-
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- mListView.post(this::tryHighlight);
- }
-
- private void tryHighlight() {
- if (mPosHighlight < 0 || mListView.getChildCount() == 0) {
- return;
- }
- if (!highlightIfVisible(mListView.getFirstVisiblePosition(),
- mListView.getLastVisiblePosition())) {
- mListView.smoothScrollToPosition(mPosHighlight);
- }
- }
-
- @Override
- public void onScrollStateChanged(AbsListView absListView, int i) { }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem,
- int visibleItemCount, int totalItemCount) {
- highlightIfVisible(firstVisibleItem, firstVisibleItem + visibleItemCount - 1);
- }
-
- private boolean highlightIfVisible(int start, int end) {
- if (mPosHighlight < 0 || mListView.getChildCount() == 0) {
- return false;
- }
- if (start > mPosHighlight || mPosHighlight > end) {
- return false;
- }
- highlightView(mListView.getChildAt(mPosHighlight - start));
-
- // finish highlight
- mListView.setOnScrollListener(null);
- mListView.removeOnLayoutChangeListener(this);
-
- mPosHighlight = -1;
- return true;
- }
-
- @Override
- public void onMovedToScrapHeap(View view) {
- unhighlightView(view);
- }
-
- private void highlightView(View view) {
- if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
- // already highlighted
- } else {
- view.setTag(R.id.view_highlighted, true);
- view.setTag(R.id.view_unhighlight_background, view.getBackground());
- view.setBackground(getHighlightBackground());
- view.postDelayed(() -> {
- unhighlightView(view);
- }, 15000L);
- }
- }
-
- private void unhighlightView(View view) {
- if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
- Object background = view.getTag(R.id.view_unhighlight_background);
- if (background instanceof Drawable) {
- view.setBackground((Drawable) background);
- }
- view.setTag(R.id.view_unhighlight_background, null);
- view.setTag(R.id.view_highlighted, false);
- }
- }
-
- private ColorDrawable getHighlightBackground() {
- int color = ColorUtils.setAlphaComponent(Themes.getColorAccent(mListView.getContext()), 26);
- if (mColorAnimated) {
- return new ColorDrawable(color);
- }
- mColorAnimated = true;
- ColorDrawable bg = new ColorDrawable(Color.WHITE);
- ObjectAnimator anim = ObjectAnimator.ofInt(bg, "color", Color.WHITE, color);
- anim.setEvaluator(new ArgbEvaluator());
- anim.setDuration(200L);
- anim.setRepeatMode(ValueAnimator.REVERSE);
- anim.setRepeatCount(4);
- anim.start();
- return bg;
- }
-}
diff --git a/src/com/android/launcher3/util/SecureSettingsObserver.java b/src/com/android/launcher3/util/SecureSettingsObserver.java
new file mode 100644
index 0000000..48aa02b
--- /dev/null
+++ b/src/com/android/launcher3/util/SecureSettingsObserver.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+
+/**
+ * Utility class to listen for secure settings changes
+ */
+public class SecureSettingsObserver extends ContentObserver {
+
+ /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
+ public static final String NOTIFICATION_BADGING = "notification_badging";
+
+ private final ContentResolver mResolver;
+ private final String mKeySetting;
+ private final int mDefaultValue;
+ private final OnChangeListener mOnChangeListener;
+
+ public SecureSettingsObserver(ContentResolver resolver, OnChangeListener listener,
+ String keySetting, int defaultValue) {
+ super(new Handler());
+
+ mResolver = resolver;
+ mOnChangeListener = listener;
+ mKeySetting = keySetting;
+ mDefaultValue = defaultValue;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mOnChangeListener.onSettingsChanged(getValue());
+ }
+
+ public boolean getValue() {
+ return Settings.Secure.getInt(mResolver, mKeySetting, mDefaultValue) == 1;
+ }
+
+ public void register() {
+ mResolver.registerContentObserver(Settings.Secure.getUriFor(mKeySetting), false, this);
+ }
+
+ public ContentResolver getResolver() {
+ return mResolver;
+ }
+
+ public void dispatchOnChange() {
+ onChange(true);
+ }
+
+ public void unregister() {
+ mResolver.unregisterContentObserver(this);
+ }
+
+ public interface OnChangeListener {
+ void onSettingsChanged(boolean isEnabled);
+ }
+
+ public static SecureSettingsObserver newNotificationSettingsObserver(Context context,
+ OnChangeListener listener) {
+ return new SecureSettingsObserver(
+ context.getContentResolver(), listener, NOTIFICATION_BADGING, 1);
+ }
+}
diff --git a/src/com/android/launcher3/util/SettingsObserver.java b/src/com/android/launcher3/util/SettingsObserver.java
deleted file mode 100644
index 6baa242..0000000
--- a/src/com/android/launcher3/util/SettingsObserver.java
+++ /dev/null
@@ -1,100 +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;
-
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.provider.Settings;
-
-public interface SettingsObserver {
-
- /**
- * Registers the content observer to call {@link #onSettingChanged(boolean)} when any of the
- * passed settings change. The value passed to onSettingChanged() is based on the key setting.
- */
- void register(String keySetting, String ... dependentSettings);
- void unregister();
- void onSettingChanged(boolean keySettingEnabled);
-
-
- abstract class Secure extends ContentObserver implements SettingsObserver {
- private ContentResolver mResolver;
- private String mKeySetting;
-
- public Secure(ContentResolver resolver) {
- super(new Handler());
- mResolver = resolver;
- }
-
- @Override
- public void register(String keySetting, String ... dependentSettings) {
- mKeySetting = keySetting;
- mResolver.registerContentObserver(
- Settings.Secure.getUriFor(mKeySetting), false, this);
- for (String setting : dependentSettings) {
- mResolver.registerContentObserver(
- Settings.Secure.getUriFor(setting), false, this);
- }
- onChange(true);
- }
-
- @Override
- public void unregister() {
- mResolver.unregisterContentObserver(this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- onSettingChanged(Settings.Secure.getInt(mResolver, mKeySetting, 1) == 1);
- }
- }
-
- abstract class System extends ContentObserver implements SettingsObserver {
- private ContentResolver mResolver;
- private String mKeySetting;
-
- public System(ContentResolver resolver) {
- super(new Handler());
- mResolver = resolver;
- }
-
- @Override
- public void register(String keySetting, String ... dependentSettings) {
- mKeySetting = keySetting;
- mResolver.registerContentObserver(
- Settings.System.getUriFor(mKeySetting), false, this);
- for (String setting : dependentSettings) {
- mResolver.registerContentObserver(
- Settings.System.getUriFor(setting), false, this);
- }
- onChange(true);
- }
-
- @Override
- public void unregister() {
- mResolver.unregisterContentObserver(this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- onSettingChanged(Settings.System.getInt(mResolver, mKeySetting, 1) == 1);
- }
- }
-}
diff --git a/src/com/android/launcher3/views/ButtonPreference.java b/src/com/android/launcher3/views/ButtonPreference.java
deleted file mode 100644
index fdcf2ca..0000000
--- a/src/com/android/launcher3/views/ButtonPreference.java
+++ /dev/null
@@ -1,65 +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.views;
-
-import android.content.Context;
-import android.preference.Preference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Extension of {@link Preference} which makes the widget layout clickable.
- *
- * @see #setWidgetLayoutResource(int)
- */
-public class ButtonPreference extends Preference {
-
- private boolean mWidgetFrameVisible = false;
-
- public ButtonPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- public ButtonPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public ButtonPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public ButtonPreference(Context context) {
- super(context);
- }
-
- public void setWidgetFrameVisible(boolean isVisible) {
- if (mWidgetFrameVisible != isVisible) {
- mWidgetFrameVisible = isVisible;
- notifyChanged();
- }
- }
-
- @Override
- protected void onBindView(View view) {
- super.onBindView(view);
-
- ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame);
- if (widgetFrame != null) {
- widgetFrame.setVisibility(mWidgetFrameVisible ? View.VISIBLE : View.GONE);
- }
- }
-}
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsRow.java b/src_plugins/com/android/systemui/plugins/AllAppsRow.java
new file mode 100644
index 0000000..c003fc1
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/AllAppsRow.java
@@ -0,0 +1,51 @@
+/*
+ * 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.systemui.plugins;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this plugin interface to add a row of views to the top of the all apps drawer.
+ */
+@ProvidesInterface(action = AllAppsRow.ACTION, version = AllAppsRow.VERSION)
+public interface AllAppsRow extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_ACTIONS";
+ int VERSION = 1;
+
+ /**
+ * Setup the row and return the parent view.
+ * @param parent The ViewGroup to which launcher will add this row.
+ */
+ View setup(ViewGroup parent);
+
+ /**
+ * @return The height to reserve in all apps for your views.
+ */
+ int getExpectedHeight();
+
+ /**
+ * Update launcher whenever {@link #getExpectedHeight()} changes.
+ */
+ void setOnHeightUpdatedListener(OnHeightUpdatedListener onHeightUpdatedListener);
+
+ interface OnHeightUpdatedListener {
+ void onHeightUpdated();
+ }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index fcb2abe..31dbb34 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -31,6 +31,10 @@
public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass) {
}
+ public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass,
+ boolean allowMultiple) {
+ }
+
public void removePluginListener(PluginListener<? extends Plugin> listener) {
}
}
diff --git a/tests/Android.mk b/tests/Android.mk
index 7cba33a..d808873 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -27,7 +27,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \
../quickstep/src/com/android/quickstep/SwipeUpSetting.java \
- ../src/com/android/launcher3/TestProtocol.java
+ ../src/com/android/launcher3/util/SecureSettingsObserver.java \
+ ../src/com/android/launcher3/TestProtocol.java \
LOCAL_SDK_VERSION := current
LOCAL_MODULE := ub-launcher-aosp-tapl