diff --git a/quickstep/src/com/android/launcher3/LauncherLifecycleListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java similarity index 78% rename from quickstep/src/com/android/launcher3/LauncherLifecycleListener.java rename to quickstep/src/com/android/launcher3/LauncherInitListener.java index 82c646d89c..28bd701a48 100644 --- a/quickstep/src/com/android/launcher3/LauncherLifecycleListener.java +++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * 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. @@ -16,23 +16,19 @@ package com.android.launcher3; import android.animation.AnimatorSet; -import android.annotation.Nullable; import android.annotation.TargetApi; import android.os.Build; import android.os.CancellationSignal; import android.view.RemoteAnimationTarget; import com.android.launcher3.uioverrides.QuickstepLauncher; -import com.android.quickstep.util.ActivityLifecycleListener; +import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.RemoteAnimationProvider; import java.util.function.BiPredicate; -/** - * {@link ActivityLifecycleListener} for the in-launcher recents. - */ @TargetApi(Build.VERSION_CODES.P) -public class LauncherLifecycleListener extends ActivityLifecycleListener { +public class LauncherInitListener extends ActivityInitListener { private RemoteAnimationProvider mRemoteAnimationProvider; @@ -40,16 +36,13 @@ public class LauncherLifecycleListener extends ActivityLifecycleListener onInitListener, - @Nullable Runnable onDestroyListener) { - super(onInitListener, onDestroyListener, Launcher.ACTIVITY_TRACKER); + public LauncherInitListener(BiPredicate onInitListener) { + super(onInitListener, Launcher.ACTIVITY_TRACKER); } @Override - public boolean handleActivityReady(Launcher launcher, boolean alreadyOnHome) { + public boolean handleInit(Launcher launcher, boolean alreadyOnHome) { if (mRemoteAnimationProvider != null) { QuickstepTransitionManager appTransitionManager = ((QuickstepLauncher) launcher).getAppTransitionManager(); @@ -75,7 +68,7 @@ public class LauncherLifecycleListener extends ActivityLifecycleListener, protected final BaseActivityInterface mActivityInterface; protected final InputConsumerProxy mInputConsumerProxy; - protected final ActivityLifecycleListener mActivityInitListener; + protected final ActivityInitListener mActivityInitListener; // Callbacks to be made once the recents animation starts private final ArrayList mRecentsAnimationStartCallbacks = new ArrayList<>(); private final OnScrollChangedListener mOnRecentsScrollListener = this::onRecentsViewScroll; @@ -329,8 +328,7 @@ public abstract class AbsSwipeUpHandler, InputConsumerController inputConsumer) { super(context, deviceState, gestureState); mActivityInterface = gestureState.getActivityInterface(); - mActivityInitListener = mActivityInterface.createActivityLifecycleListener( - this::onActivityInit, this::onActivityDestroy); + mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit); mInputConsumerProxy = new InputConsumerProxy(context, /* rotationSupplier = */ () -> { if (mRecentsView == null) { @@ -521,11 +519,6 @@ public abstract class AbsSwipeUpHandler, return true; } - private void onActivityDestroy() { - ActiveGestureLog.INSTANCE.addLog("Launcher activity destroyed", LAUNCHER_DESTROYED); - onGestureCancelled(); - } - /** * Return true if the window should be translated horizontally if the recents view scrolls */ diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 55235185ef..274b686ea4 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -60,7 +60,7 @@ import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.NavigationMode; import com.android.launcher3.views.ScrimView; -import com.android.quickstep.util.ActivityLifecycleListener; +import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -123,17 +123,8 @@ public abstract class BaseActivityInterface callback); - /** - * Creates a activity listener for activity initialized and/or destroyed. One or both of these - * listeners must be provided. - * - * @param onInitListener a callback made when the activity is initialized. The callback should - * return true to continue receiving callbacks (ie. for if the activity is - * recreated). - * @param onDestroyListener a callback made when the activity is destroyed. - */ - public abstract ActivityLifecycleListener createActivityLifecycleListener( - @Nullable Predicate onInitListener, @Nullable Runnable onDestroyListener); + public abstract ActivityInitListener createActivityInitListener( + Predicate onInitListener); /** * Sets a callback to be run when an activity launch happens while launcher is not yet resumed. diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java index a658566496..ae9fb0b385 100644 --- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java +++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java @@ -36,7 +36,7 @@ import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.DisplayController; import com.android.quickstep.GestureState.GestureEndTarget; import com.android.quickstep.fallback.RecentsState; -import com.android.quickstep.util.ActivityLifecycleListener; +import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.views.RecentsView; @@ -88,12 +88,10 @@ public final class FallbackActivityInterface extends } @Override - public ActivityLifecycleListener createActivityLifecycleListener( - @Nullable Predicate onInitListener, @Nullable Runnable onDestroyListener) { - return new ActivityLifecycleListener<>( - (activity, alreadyOnHome) -> onInitListener.test(alreadyOnHome), - onDestroyListener, - RecentsActivity.ACTIVITY_TRACKER); + public ActivityInitListener createActivityInitListener( + Predicate onInitListener) { + return new ActivityInitListener<>((activity, alreadyOnHome) -> + onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER); } @Nullable diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java index cb54d2e8c2..9ff941671b 100644 --- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java +++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java @@ -37,7 +37,7 @@ import androidx.annotation.UiThread; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils; -import com.android.launcher3.LauncherLifecycleListener; +import com.android.launcher3.LauncherInitListener; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.statehandlers.DepthController; @@ -49,7 +49,7 @@ import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.NavigationMode; import com.android.quickstep.GestureState.GestureEndTarget; -import com.android.quickstep.util.ActivityLifecycleListener; +import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.views.RecentsView; @@ -136,10 +136,9 @@ public final class LauncherActivityInterface extends } @Override - public ActivityLifecycleListener createActivityLifecycleListener( - @Nullable Predicate onInitListener, @Nullable Runnable onDestroyListener) { - return new LauncherLifecycleListener((activity, alreadyOnHome) -> - onInitListener.test(alreadyOnHome), onDestroyListener); + public ActivityInitListener createActivityInitListener(Predicate onInitListener) { + return new LauncherInitListener((activity, alreadyOnHome) -> + onInitListener.test(alreadyOnHome)); } @Override diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java index 0fdd8b5e36..60065fb16c 100644 --- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java +++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java @@ -37,7 +37,7 @@ public class ActiveGestureErrorDetector { ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED, - FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED, + FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, /** * These GestureEvents are specifically associated to state flags that get set in @@ -162,13 +162,6 @@ public class ActiveGestureErrorDetector { + "before/without setting end target to new task", writer); break; - case LAUNCHER_DESTROYED: - errorDetected |= printErrorIfTrue( - true, - prefix, - /* errorMessage= */ "Launcher destroyed mid-gesture", - writer); - break; case STATE_GESTURE_COMPLETED: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.MOTION_UP), diff --git a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java new file mode 100644 index 0000000000..aeec36f63c --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import com.android.launcher3.BaseActivity; +import com.android.launcher3.util.ActivityTracker; +import com.android.launcher3.util.ActivityTracker.SchedulerCallback; + +import java.util.function.BiPredicate; + +public class ActivityInitListener implements + SchedulerCallback { + + private BiPredicate mOnInitListener; + private final ActivityTracker mActivityTracker; + + private boolean mIsRegistered = false; + + /** + * @param onInitListener a callback made when the activity is initialized. The callback should + * return true to continue receiving callbacks (ie. for if the activity is + * recreated). + */ + public ActivityInitListener(BiPredicate onInitListener, + ActivityTracker tracker) { + mOnInitListener = onInitListener; + mActivityTracker = tracker; + } + + @Override + public final boolean init(T activity, boolean alreadyOnHome) { + if (!mIsRegistered) { + // Don't receive any more updates + return false; + } + return handleInit(activity, alreadyOnHome); + } + + protected boolean handleInit(T activity, boolean alreadyOnHome) { + return mOnInitListener.test(activity, alreadyOnHome); + } + + /** + * Registers the activity-created listener. If the activity is already created, then the + * callback provided in the constructor will be called synchronously. + */ + public void register() { + mIsRegistered = true; + mActivityTracker.registerCallback(this); + } + + /** + * After calling this, we won't {@link #init} even when the activity is ready. + */ + public void unregister() { + mActivityTracker.unregisterCallback(this); + mIsRegistered = false; + mOnInitListener = null; + } +} diff --git a/quickstep/src/com/android/quickstep/util/ActivityLifecycleListener.java b/quickstep/src/com/android/quickstep/util/ActivityLifecycleListener.java deleted file mode 100644 index 1edf1888e8..0000000000 --- a/quickstep/src/com/android/quickstep/util/ActivityLifecycleListener.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.quickstep.util; - -import android.util.Log; - -import androidx.annotation.Nullable; - -import com.android.launcher3.BaseActivity; -import com.android.launcher3.util.ActivityTracker; -import com.android.launcher3.util.ActivityTracker.SchedulerCallback; - -import java.util.function.BiPredicate; - -/** - * Listener for activity initialized and/or destroyed. - */ -public class ActivityLifecycleListener implements - SchedulerCallback { - - private static final String TAG = "ActivityLifecycleListener"; - - @Nullable private final BiPredicate mOnInitListener; - @Nullable private final Runnable mOnDestroyListener; - private final ActivityTracker mActivityTracker; - - private boolean mIsRegistered = false; - - /** - * One or both of {@code onInitListener} and {@code onInitListener} must be provided, otherwise - * the created instance will effectively be a no-op. - * - * @param onInitListener a callback made when the activity is initialized. The callback should - * return true to continue receiving callbacks (ie. for if the activity is - * recreated). - * @param onDestroyListener a callback made when the activity is destroyed. - */ - public ActivityLifecycleListener( - @Nullable BiPredicate onInitListener, - @Nullable Runnable onDestroyListener, - ActivityTracker tracker) { - if (onInitListener == null && onDestroyListener == null) { - throw new IllegalArgumentException("Both listeners cannot be null"); - } - mOnInitListener = onInitListener; - mOnDestroyListener = onDestroyListener; - mActivityTracker = tracker; - } - - @Override - public final boolean onActivityReady(T activity, boolean alreadyOnHome) { - if (!mIsRegistered) { - // Don't receive any more updates - return false; - } - return handleActivityReady(activity, alreadyOnHome); - } - - protected boolean handleActivityReady(T activity, boolean alreadyOnHome) { - if (mOnInitListener == null) { - Log.e(TAG, "Cannot handle init: init listener is null", new Exception()); - return false; - } - return mOnInitListener.test(activity, alreadyOnHome); - } - - @Override - public void onActivityDestroyed() { - if (mOnDestroyListener == null) { - Log.e(TAG, "Cannot clean up: destroy listener is null", new Exception()); - return; - } - mOnDestroyListener.run(); - } - - /** - * Registers the activity-created listener. If the activity is already created, then the - * callback provided in the constructor will be called synchronously. - */ - public void register() { - mIsRegistered = true; - mActivityTracker.registerCallback(this, getType()); - } - - /** - * After calling this, we won't call {@link #onActivityReady} even when the activity is ready. - */ - public void unregister() { - mActivityTracker.unregisterCallback(this, getType()); - mIsRegistered = false; - } - - private int getType() { - return mOnInitListener != null && mOnDestroyListener != null - ? ActivityTracker.TYPE_BOTH - : (mOnInitListener != null - ? ActivityTracker.TYPE_INIT - : ActivityTracker.TYPE_DESTROY); - } -} diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index efdd5e15e7..55ede6cecb 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -31,7 +31,6 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; -import android.util.Pair; import android.util.Patterns; import android.util.Xml; @@ -45,7 +44,7 @@ import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.qsb.QsbContainerView; import com.android.launcher3.util.IntArray; -import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.Partner; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.LauncherWidgetHolder; @@ -76,19 +75,16 @@ public class AutoInstallsLayout { static AutoInstallsLayout get(Context context, LauncherWidgetHolder appWidgetHolder, LayoutParserCallback callback) { - Pair customizationApkInfo = PackageManagerHelper.findSystemApk( - ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager()); - if (customizationApkInfo == null) { + Partner partner = Partner.get(context.getPackageManager(), ACTION_LAUNCHER_CUSTOMIZATION); + if (partner == null) { return null; } - String pkg = customizationApkInfo.first; - Resources targetRes = customizationApkInfo.second; InvariantDeviceProfile grid = LauncherAppState.getIDP(context); // Try with grid size and hotseat count String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT, grid.numColumns, grid.numRows, grid.numDatabaseHotseatIcons); - int layoutId = targetRes.getIdentifier(layoutName, "xml", pkg); + int layoutId = partner.getXmlResId(layoutName); // Try with only grid size if (layoutId == 0) { @@ -96,21 +92,21 @@ public class AutoInstallsLayout { + " not found. Trying layout without hosteat"); layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES, grid.numColumns, grid.numRows); - layoutId = targetRes.getIdentifier(layoutName, "xml", pkg); + layoutId = partner.getXmlResId(layoutName); } // Try the default layout if (layoutId == 0) { Log.d(TAG, "Formatted layout: " + layoutName + " not found. Trying the default layout"); - layoutId = targetRes.getIdentifier(LAYOUT_RES, "xml", pkg); + layoutId = partner.getXmlResId(LAYOUT_RES); } if (layoutId == 0) { - Log.e(TAG, "Layout definition not found in package: " + pkg); + Log.e(TAG, "Layout definition not found in package: " + partner.getPackageName()); return null; } - return new AutoInstallsLayout(context, appWidgetHolder, callback, targetRes, layoutId, - TAG_WORKSPACE); + return new AutoInstallsLayout(context, appWidgetHolder, callback, partner.getResources(), + layoutId, TAG_WORKSPACE); } // Object Tags diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java index af13beac1d..c69ae4dac8 100644 --- a/src/com/android/launcher3/DefaultLayoutParser.java +++ b/src/com/android/launcher3/DefaultLayoutParser.java @@ -19,6 +19,7 @@ import android.util.Log; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; +import com.android.launcher3.util.Partner; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.LauncherWidgetHolder; @@ -51,6 +52,9 @@ public class DefaultLayoutParser extends AutoInstallsLayout { private static final String ATTR_SHORTCUT_ID = "shortcutId"; private static final String ATTR_PACKAGE_NAME = "packageName"; + public static final String RES_PARTNER_FOLDER = "partner_folder"; + public static final String RES_PARTNER_DEFAULT_LAYOUT = "partner_default_layout"; + // TODO: Remove support for this broadcast, instead use widget options to send bind time options private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE = "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE"; @@ -278,10 +282,9 @@ public class DefaultLayoutParser extends AutoInstallsLayout { // Folder contents come from an external XML resource final Partner partner = Partner.get(mPackageManager); if (partner != null) { - final Resources partnerRes = partner.getResources(); - final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER, - "xml", partner.getPackageName()); + final int resId = partner.getXmlResId(RES_PARTNER_FOLDER); if (resId != 0) { + final Resources partnerRes = partner.getResources(); final XmlPullParser partnerParser = partnerRes.getXml(resId); beginDocument(partnerParser, TAG_FOLDER); diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 1f97535b4c..594d7cbd82 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -60,6 +60,7 @@ import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.launcher3.util.Partner; import com.android.launcher3.util.Themes; import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.window.WindowManagerProxy; @@ -112,6 +113,11 @@ public class InvariantDeviceProfile { static final int INDEX_TWO_PANEL_PORTRAIT = 2; static final int INDEX_TWO_PANEL_LANDSCAPE = 3; + /** These resources are used to override the device profile */ + private static final String RES_GRID_NUM_ROWS = "grid_num_rows"; + private static final String RES_GRID_NUM_COLUMNS = "grid_num_columns"; + private static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp"; + /** * Number of icons per row and column in the workspace. */ @@ -567,8 +573,24 @@ public class InvariantDeviceProfile { */ private void applyPartnerDeviceProfileOverrides(Context context, DisplayMetrics dm) { Partner p = Partner.get(context.getPackageManager()); - if (p != null) { - p.applyInvariantDeviceProfileOverrides(this, dm); + if (p == null) { + return; + } + try { + int numRows = p.getIntValue(RES_GRID_NUM_ROWS, -1); + int numColumns = p.getIntValue(RES_GRID_NUM_COLUMNS, -1); + float iconSizePx = p.getDimenValue(RES_GRID_ICON_SIZE_DP, -1); + + if (numRows > 0 && numColumns > 0) { + this.numRows = numRows; + this.numColumns = numColumns; + } + if (iconSizePx > 0) { + this.iconSize[InvariantDeviceProfile.INDEX_DEFAULT] = + Utilities.dpiFromPx(iconSizePx, dm.densityDpi); + } + } catch (Resources.NotFoundException ex) { + Log.e(TAG, "Invalid Partner grid resource!", ex); } } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index d002c2b901..8ddbbaaf2e 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import static com.android.launcher3.DefaultLayoutParser.RES_PARTNER_DEFAULT_LAYOUT; import static com.android.launcher3.provider.LauncherDbUtils.copyTable; import static com.android.launcher3.provider.LauncherDbUtils.dropTable; import static com.android.launcher3.provider.LauncherDbUtils.tableExists; @@ -34,7 +35,6 @@ import android.content.Intent; import android.content.OperationApplicationException; import android.content.SharedPreferences; import android.content.pm.ProviderInfo; -import android.content.res.Resources; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.SQLException; @@ -70,6 +70,7 @@ import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.NoLocaleSQLiteHelper; import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.Partner; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.LauncherWidgetHolder; @@ -544,13 +545,11 @@ public class LauncherProvider extends ContentProvider { } if (loader == null) { final Partner partner = Partner.get(getContext().getPackageManager()); - if (partner != null && partner.hasDefaultLayout()) { - final Resources partnerRes = partner.getResources(); - int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT, - "xml", partner.getPackageName()); + if (partner != null) { + int workspaceResId = partner.getXmlResId(RES_PARTNER_DEFAULT_LAYOUT); if (workspaceResId != 0) { loader = new DefaultLayoutParser(getContext(), widgetHolder, - mOpenHelper, partnerRes, workspaceResId); + mOpenHelper, partner.getResources(), workspaceResId); } } } diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java deleted file mode 100644 index 2e27f32226..0000000000 --- a/src/com/android/launcher3/Partner.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2014 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.util.PackageManagerHelper.findSystemApk; - -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.Pair; - -import java.io.File; - -/** - * Utilities to discover and interact with partner customizations. There can - * only be one set of customizations on a device, and it must be bundled with - * the system. - */ -public class Partner { - - static final String TAG = "Launcher.Partner"; - - /** Marker action used to discover partner */ - private static final String - ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION"; - - public static final String RES_FOLDER = "partner_folder"; - public static final String RES_WALLPAPERS = "partner_wallpapers"; - public static final String RES_DEFAULT_LAYOUT = "partner_default_layout"; - - public static final String RES_DEFAULT_WALLPAPER_HIDDEN = "default_wallpapper_hidden"; - public static final String RES_SYSTEM_WALLPAPER_DIR = "system_wallpaper_directory"; - - public static final String RES_REQUIRE_FIRST_RUN_FLOW = "requires_first_run_flow"; - - /** These resources are used to override the device profile */ - public static final String RES_GRID_NUM_ROWS = "grid_num_rows"; - public static final String RES_GRID_NUM_COLUMNS = "grid_num_columns"; - public static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp"; - - /** - * Find and return partner details, or {@code null} if none exists. - */ - public static synchronized Partner get(PackageManager pm) { - Pair apkInfo = findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm); - return apkInfo != null ? new Partner(apkInfo.first, apkInfo.second) : null; - } - - private final String mPackageName; - private final Resources mResources; - - private Partner(String packageName, Resources res) { - mPackageName = packageName; - mResources = res; - } - - public String getPackageName() { - return mPackageName; - } - - public Resources getResources() { - return mResources; - } - - public boolean hasDefaultLayout() { - int defaultLayout = getResources().getIdentifier(Partner.RES_DEFAULT_LAYOUT, - "xml", getPackageName()); - return defaultLayout != 0; - } - - public boolean hasFolder() { - int folder = getResources().getIdentifier(Partner.RES_FOLDER, - "xml", getPackageName()); - return folder != 0; - } - - public boolean hideDefaultWallpaper() { - int resId = getResources().getIdentifier(RES_DEFAULT_WALLPAPER_HIDDEN, "bool", - getPackageName()); - return resId != 0 && getResources().getBoolean(resId); - } - - public File getWallpaperDirectory() { - int resId = getResources().getIdentifier(RES_SYSTEM_WALLPAPER_DIR, "string", - getPackageName()); - return (resId != 0) ? new File(getResources().getString(resId)) : null; - } - - public boolean requiresFirstRunFlow() { - int resId = getResources().getIdentifier(RES_REQUIRE_FIRST_RUN_FLOW, "bool", - getPackageName()); - return resId != 0 && getResources().getBoolean(resId); - } - - public void applyInvariantDeviceProfileOverrides(InvariantDeviceProfile inv, DisplayMetrics dm) { - int numRows = -1; - int numColumns = -1; - float iconSize = -1; - - try { - int resId = getResources().getIdentifier(RES_GRID_NUM_ROWS, - "integer", getPackageName()); - if (resId > 0) { - numRows = getResources().getInteger(resId); - } - - resId = getResources().getIdentifier(RES_GRID_NUM_COLUMNS, - "integer", getPackageName()); - if (resId > 0) { - numColumns = getResources().getInteger(resId); - } - - resId = getResources().getIdentifier(RES_GRID_ICON_SIZE_DP, - "dimen", getPackageName()); - if (resId > 0) { - int px = getResources().getDimensionPixelSize(resId); - iconSize = Utilities.dpiFromPx((float) px, dm.densityDpi); - } - } catch (Resources.NotFoundException ex) { - Log.e(TAG, "Invalid Partner grid resource!", ex); - return; - } - - if (numRows > 0 && numColumns > 0) { - inv.numRows = numRows; - inv.numColumns = numColumns; - } - - if (iconSize > 0) { - inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT] = iconSize; - } - } -} diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 5c6fb5086b..9064217d71 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -89,14 +89,6 @@ public final class FeatureFlags { getDebugFlag("ENABLE_FLOATING_SEARCH_BAR", false, "Keep All Apps search bar at the bottom (but above keyboard if open)"); - public static final BooleanFlag ENABLE_QUICK_LAUNCH_V2 = new DeviceFlag( - "ENABLE_QUICK_LAUNCH_V2", false, "Use quick launch v2 " - + "behavior. Quick search and quick launch v1 would be unavailable if this is enabled"); - - public static final BooleanFlag GBOARD_UPDATE_ENTER_KEY = new DeviceFlag( - "GBOARD_UPDATE_ENTER_KEY", false, "Update gBoard enter key " - + "icon dynamically based on top search content for Quick Launch V2"); - public static final BooleanFlag ENABLE_HIDE_HEADER = new DeviceFlag("ENABLE_HIDE_HEADER", true, "Hide header on keyboard before typing in all apps"); @@ -374,6 +366,11 @@ public final class FeatureFlags { "Enable the ability to tap a staged app during split select to launch it in full screen" ); + public static final BooleanFlag ENABLE_FORCED_MONO_ICON = getDebugFlag( + "ENABLE_FORCED_MONO_ICON", false, + "Enable the ability to generate monochromatic icons, if it is not provided by the app" + ); + public static void initialize(Context context) { synchronized (sDebugFlags) { for (DebugFlag flag : sDebugFlags) { diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java index 6d127b3998..981e3a65a4 100644 --- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java +++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java @@ -74,7 +74,7 @@ public abstract class BaseItemDragListener implements View.OnDragListener, DragS } @Override - public boolean onActivityReady(Launcher launcher, boolean alreadyOnHome) { + public boolean init(Launcher launcher, boolean alreadyOnHome) { AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome); launcher.getStateManager().goToState(NORMAL, alreadyOnHome /* animated */); launcher.getDragLayer().setOnDragListener(this); diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java index fb924c14d1..af43ae83e2 100644 --- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java +++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java @@ -72,8 +72,8 @@ public class PinItemDragListener extends BaseItemDragListener { } @Override - public boolean onActivityReady(Launcher launcher, boolean alreadyOnHome) { - super.onActivityReady(launcher, alreadyOnHome); + public boolean init(Launcher launcher, boolean alreadyOnHome) { + super.init(launcher, alreadyOnHome); if (!alreadyOnHome) { launcher.useFadeOutAnimationForLauncherStart(mCancelSignal); } diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java index 5508c49410..57fa8a256b 100644 --- a/src/com/android/launcher3/icons/LauncherIcons.java +++ b/src/com/android/launcher3/icons/LauncherIcons.java @@ -16,7 +16,10 @@ package com.android.launcher3.icons; +import static com.android.launcher3.config.FeatureFlags.ENABLE_FORCED_MONO_ICON; + import android.content.Context; +import android.graphics.drawable.Drawable; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.graphics.IconShape; @@ -68,6 +71,8 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { private LauncherIcons next; + private MonochromeIconFactory mMonochromeIconFactory; + protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) { super(context, fillResIconDpi, iconBitmapSize, IconShape.getShape().enableShapeDetection()); mMonoIconEnabled = Themes.isThemedIconEnabled(context); @@ -90,6 +95,18 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { } } + @Override + protected Drawable getMonochromeDrawable(Drawable base) { + Drawable mono = super.getMonochromeDrawable(base); + if (mono != null || !ENABLE_FORCED_MONO_ICON.get()) { + return mono; + } + if (mMonochromeIconFactory == null) { + mMonochromeIconFactory = new MonochromeIconFactory(mIconBitmapSize); + } + return mMonochromeIconFactory.wrap(base); + } + @Override public void close() { recycle(); diff --git a/src/com/android/launcher3/icons/MonochromeIconFactory.java b/src/com/android/launcher3/icons/MonochromeIconFactory.java new file mode 100644 index 0000000000..511dcc736e --- /dev/null +++ b/src/com/android/launcher3/icons/MonochromeIconFactory.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2022 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.icons; + +import static android.graphics.Paint.FILTER_BITMAP_FLAG; + +import android.annotation.TargetApi; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BlendMode; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; + +import androidx.annotation.WorkerThread; + +import com.android.launcher3.icons.BaseIconFactory.ClippedMonoDrawable; + +import java.nio.ByteBuffer; + +/** + * Utility class to generate monochrome icons version for a given drawable. + */ +@TargetApi(Build.VERSION_CODES.TIRAMISU) +public class MonochromeIconFactory extends Drawable { + + private final Bitmap mFlatBitmap; + private final Canvas mFlatCanvas; + private final Paint mCopyPaint; + + private final Bitmap mAlphaBitmap; + private final Canvas mAlphaCanvas; + private final byte[] mPixels; + + private final int mBitmapSize; + private final int mEdgePixelLength; + + private final Paint mDrawPaint; + private final Rect mSrcRect; + + MonochromeIconFactory(int iconBitmapSize) { + float extraFactor = AdaptiveIconDrawable.getExtraInsetFraction(); + float viewPortScale = 1 / (1 + 2 * extraFactor); + mBitmapSize = Math.round(iconBitmapSize * 2 * viewPortScale); + mPixels = new byte[mBitmapSize * mBitmapSize]; + mEdgePixelLength = mBitmapSize * (mBitmapSize - iconBitmapSize) / 2; + + mFlatBitmap = Bitmap.createBitmap(mBitmapSize, mBitmapSize, Config.ARGB_8888); + mFlatCanvas = new Canvas(mFlatBitmap); + + mAlphaBitmap = Bitmap.createBitmap(mBitmapSize, mBitmapSize, Config.ALPHA_8); + mAlphaCanvas = new Canvas(mAlphaBitmap); + + mDrawPaint = new Paint(FILTER_BITMAP_FLAG); + mDrawPaint.setColor(Color.WHITE); + mSrcRect = new Rect(0, 0, mBitmapSize, mBitmapSize); + + mCopyPaint = new Paint(FILTER_BITMAP_FLAG); + mCopyPaint.setBlendMode(BlendMode.SRC); + + // Crate a color matrix which converts the icon to grayscale and then uses the average + // of RGB components as the alpha component. + ColorMatrix satMatrix = new ColorMatrix(); + satMatrix.setSaturation(0); + float[] vals = satMatrix.getArray(); + vals[15] = vals[16] = vals[17] = .3333f; + vals[18] = vals[19] = 0; + mCopyPaint.setColorFilter(new ColorMatrixColorFilter(vals)); + } + + private void drawDrawable(Drawable drawable) { + if (drawable != null) { + drawable.setBounds(0, 0, mBitmapSize, mBitmapSize); + drawable.draw(mFlatCanvas); + } + } + + /** + * Creates a monochrome version of the provided drawable + */ + @WorkerThread + public Drawable wrap(Drawable icon) { + if (icon instanceof AdaptiveIconDrawable) { + AdaptiveIconDrawable aid = (AdaptiveIconDrawable) icon; + mFlatCanvas.drawColor(Color.BLACK); + drawDrawable(aid.getBackground()); + drawDrawable(aid.getForeground()); + generateMono(); + return new ClippedMonoDrawable(this); + } else { + mFlatCanvas.drawColor(Color.WHITE); + drawDrawable(icon); + generateMono(); + return this; + } + } + + @WorkerThread + private void generateMono() { + mAlphaCanvas.drawBitmap(mFlatBitmap, 0, 0, mCopyPaint); + + // Scale the end points: + ByteBuffer buffer = ByteBuffer.wrap(mPixels); + buffer.rewind(); + mAlphaBitmap.copyPixelsToBuffer(buffer); + + int min = 0xFF; + int max = 0; + for (byte b : mPixels) { + min = Math.min(min, b & 0xFF); + max = Math.max(max, b & 0xFF); + } + + if (min < max) { + // rescale pixels to increase contrast + float range = max - min; + + // In order to check if the colors should be flipped, we just take the average color + // of top and bottom edge which should correspond to be background color. If the edge + // colors have more opacity, we flip the colors; + int sum = 0; + for (int i = 0; i < mEdgePixelLength; i++) { + sum += (mPixels[i] & 0xFF); + sum += (mPixels[mPixels.length - 1 - i] & 0xFF); + } + float edgeAverage = sum / (mEdgePixelLength * 2f); + float edgeMapped = (edgeAverage - min) / range; + boolean flipColor = edgeMapped > .5f; + + for (int i = 0; i < mPixels.length; i++) { + int p = mPixels[i] & 0xFF; + int p2 = Math.round((p - min) * 0xFF / range); + mPixels[i] = flipColor ? (byte) (255 - p2) : (byte) (p2); + } + buffer.rewind(); + mAlphaBitmap.copyPixelsFromBuffer(buffer); + } + } + + @Override + public void draw(Canvas canvas) { + canvas.drawBitmap(mAlphaBitmap, mSrcRect, getBounds(), mDrawPaint); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int i) { + mDrawPaint.setAlpha(i); + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + mDrawPaint.setColorFilter(colorFilter); + } +} diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java index 5f93a66d03..7af1a13d67 100644 --- a/src/com/android/launcher3/util/ActivityTracker.java +++ b/src/com/android/launcher3/util/ActivityTracker.java @@ -20,6 +20,8 @@ import androidx.annotation.Nullable; import com.android.launcher3.BaseActivity; import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashSet; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -28,15 +30,8 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public final class ActivityTracker { - public static final int TYPE_INIT = 0; - public static final int TYPE_DESTROY = 1; - public static final int TYPE_BOTH = 2; - private WeakReference mCurrentActivity = new WeakReference<>(null); - private CopyOnWriteArrayList> mActivityReadyCallbacks = - new CopyOnWriteArrayList<>(); - private CopyOnWriteArrayList> mActivityDestroyedCallbacks = - new CopyOnWriteArrayList<>(); + private CopyOnWriteArrayList> mCallbacks = new CopyOnWriteArrayList<>(); @Nullable public R getCreatedActivity() { @@ -47,74 +42,32 @@ public final class ActivityTracker { if (mCurrentActivity.get() == activity) { mCurrentActivity.clear(); } - for (SchedulerCallback cb : mActivityDestroyedCallbacks) { - cb.onActivityDestroyed(); - unregisterCallback(cb, TYPE_DESTROY); - } } - /** Registers an activity create callback. */ + /** + * Call {@link SchedulerCallback#init(BaseActivity, boolean)} when the + * activity is ready. If the activity is already created, this is called immediately. + * + * The tracker maintains a strong ref to the callback, so it is up to the caller to return + * {@code false} in the callback OR to unregister the callback explicitly. + * + * @param callback The callback to call init() on when the activity is ready. + */ public void registerCallback(SchedulerCallback callback) { - registerCallback(callback, TYPE_INIT); - } - - /** - * Call {@link SchedulerCallback#onActivityReady(BaseActivity, boolean)} when the - * activity is ready and/or {@link SchedulerCallback#onActivityDestroyed()} when the activity - * is destroyed. - * - * If type is {@link ActivityTracker#TYPE_INIT} TYPE_INIT or - * {@link ActivityTracker#TYPE_BOTH} and the activity is already created, this - * {@link SchedulerCallback#onActivityReady(BaseActivity, boolean)} is called immediately. - * - * If type is {@link ActivityTracker#TYPE_DESTROY} or - * {@link ActivityTracker#TYPE_BOTH} and the activity is already destroyed, - * {@link SchedulerCallback#onActivityDestroyed()} is called immediately. - * - * The tracker maintains a strong ref to the callbacks, so it is up to the caller to return - * {@code false} in {@link SchedulerCallback#onActivityReady(BaseActivity, boolean)} OR to - * unregister the callback explicitly. - * - * @param callback The callback to call init() or cleanUp() on when the activity is ready or - * destroyed. - * @param type whether to use this callback on activity create, destroy or both. - */ - public void registerCallback(SchedulerCallback callback, int type) { T activity = mCurrentActivity.get(); - if (type == TYPE_INIT || type == TYPE_BOTH) { - mActivityReadyCallbacks.add(callback); - if (activity != null) { - if (!callback.onActivityReady(activity, activity.isStarted())) { - unregisterCallback(callback, TYPE_INIT); - } + mCallbacks.add(callback); + if (activity != null) { + if (!callback.init(activity, activity.isStarted())) { + unregisterCallback(callback); } } - if (type == TYPE_DESTROY || type == TYPE_BOTH) { - mActivityDestroyedCallbacks.add(callback); - if (activity == null) { - callback.onActivityDestroyed(); - unregisterCallback(callback, TYPE_DESTROY); - } - } - } - - /** - * Unregisters a registered activity create callback. - */ - public void unregisterCallback(SchedulerCallback callback) { - unregisterCallback(callback, TYPE_INIT); } /** * Unregisters a registered callback. */ - public void unregisterCallback(SchedulerCallback callback, int type) { - if (type == TYPE_INIT || type == TYPE_BOTH) { - mActivityReadyCallbacks.remove(callback); - } - if (type == TYPE_DESTROY || type == TYPE_BOTH) { - mActivityDestroyedCallbacks.remove(callback); - } + public void unregisterCallback(SchedulerCallback callback) { + mCallbacks.remove(callback); } public boolean handleCreate(T activity) { @@ -128,8 +81,8 @@ public final class ActivityTracker { private boolean handleIntent(T activity, boolean alreadyOnHome) { boolean handled = false; - for (SchedulerCallback cb : mActivityReadyCallbacks) { - if (!cb.onActivityReady(activity, alreadyOnHome)) { + for (SchedulerCallback cb : mCallbacks) { + if (!cb.init(activity, alreadyOnHome)) { // Callback doesn't want any more updates unregisterCallback(cb); } @@ -145,11 +98,6 @@ public final class ActivityTracker { * @param alreadyOnHome Whether the activity is already started. * @return Whether to continue receiving callbacks (i.e. if the activity is recreated). */ - boolean onActivityReady(T activity, boolean alreadyOnHome); - - /** - * Called then the activity gets destroyed. - */ - default void onActivityDestroyed() { } + boolean init(T activity, boolean alreadyOnHome); } } diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java index 9ca0dd195c..8e3daf33a4 100644 --- a/src/com/android/launcher3/util/DisplayController.java +++ b/src/com/android/launcher3/util/DisplayController.java @@ -131,7 +131,10 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { * Returns whether taskbar is transient. */ public static boolean isTransientTaskbar(Context context) { - if (FORCE_PERSISTENT_TASKBAR.get()) { + // TODO(b/258604917): When running in test harness, use !sTransientTaskbarStatusForTests + // once tests are updated to expect new persistent behavior such as not allowing long press + // to stash. + if (!Utilities.IS_RUNNING_IN_TEST_HARNESS && FORCE_PERSISTENT_TASKBAR.get()) { return false; } return getNavigationMode(context) == NavigationMode.NO_BUTTON diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java index 12e8b54547..140440eee2 100644 --- a/src/com/android/launcher3/util/PackageManagerHelper.java +++ b/src/com/android/launcher3/util/PackageManagerHelper.java @@ -16,8 +16,6 @@ package com.android.launcher3.util; -import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; - import android.app.AppOpsManager; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -31,7 +29,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; -import android.content.res.Resources; import android.graphics.Rect; import android.net.Uri; import android.os.Build; @@ -40,7 +37,6 @@ import android.os.PatternMatcher; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; -import android.util.Pair; import android.widget.Toast; import androidx.annotation.NonNull; @@ -295,25 +291,6 @@ public class PackageManagerHelper { } } - /** - * Finds a system apk which had a broadcast receiver listening to a particular action. - * @param action intent action used to find the apk - * @return a pair of apk package name and the resources. - */ - public static Pair findSystemApk(String action, PackageManager pm) { - final Intent intent = new Intent(action); - for (ResolveInfo info : pm.queryBroadcastReceivers(intent, MATCH_SYSTEM_ONLY)) { - final String packageName = info.activityInfo.packageName; - try { - final Resources res = pm.getResourcesForApplication(packageName); - return Pair.create(packageName, res); - } catch (NameNotFoundException e) { - Log.w(TAG, "Failed to find resources for " + packageName); - } - } - return null; - } - /** * Returns true if the intent is a valid launch intent for a launcher activity of an app. * This is used to identify shortcuts which are different from the ones exposed by the diff --git a/src/com/android/launcher3/util/Partner.java b/src/com/android/launcher3/util/Partner.java new file mode 100644 index 0000000000..220ab566aa --- /dev/null +++ b/src/com/android/launcher3/util/Partner.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2014 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 static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.util.Log; +import android.util.Pair; + +/** + * Utilities to discover and interact with partner customizations. There can + * only be one set of customizations on a device, and it must be bundled with + * the system. + */ +public class Partner { + + static final String TAG = "Launcher.Partner"; + + /** Marker action used to discover partner */ + private static final String + ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION"; + + /** + * Find and return partner details, or {@code null} if none exists. + */ + public static Partner get(PackageManager pm) { + return get(pm, ACTION_PARTNER_CUSTOMIZATION); + } + + /** + * Find and return partner details, or {@code null} if none exists. + */ + public static Partner get(PackageManager pm, String action) { + Pair apkInfo = findSystemApk(action, pm); + return apkInfo != null ? new Partner(apkInfo.first, apkInfo.second) : null; + } + + private final String mPackageName; + private final Resources mResources; + + private Partner(String packageName, Resources res) { + mPackageName = packageName; + mResources = res; + } + + public String getPackageName() { + return mPackageName; + } + + public Resources getResources() { + return mResources; + } + + /** + * Returns the xml resource Id for the provided name, or 0 is the resource is not found + */ + public int getXmlResId(String layoutName) { + return getResources().getIdentifier(layoutName, "xml", getPackageName()); + } + + /** + * Returns the integer resource value for the provided resource name, + * or default value if the resource name is not present + */ + public int getIntValue(String resName, int defaultValue) { + int resId = getResources().getIdentifier(resName, "integer", getPackageName()); + return resId > 0 ? getResources().getInteger(resId) : defaultValue; + } + + /** + * Returns the dimension value for the provided resource name, + * or default value if the resource name is not present + */ + public float getDimenValue(String resName, int defaultValue) { + int resId = getResources().getIdentifier(resName, "dimen", getPackageName()); + return resId > 0 ? getResources().getDimension(resId) : defaultValue; + } + + /** + * Finds a system apk which had a broadcast receiver listening to a particular action. + * @param action intent action used to find the apk + * @return a pair of apk package name and the resources. + */ + private static Pair findSystemApk(String action, PackageManager pm) { + final Intent intent = new Intent(action); + for (ResolveInfo info : pm.queryBroadcastReceivers(intent, MATCH_SYSTEM_ONLY)) { + final String packageName = info.activityInfo.packageName; + try { + final Resources res = pm.getResourcesForApplication(packageName); + return Pair.create(packageName, res); + } catch (NameNotFoundException e) { + Log.w(TAG, "Failed to find resources for " + packageName); + } + } + return null; + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java index bddb5935f7..80e4116406 100644 --- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java +++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java @@ -55,7 +55,7 @@ public class SearchResultFromQsb { } /** Find the web suggestion from search suggestion's title text */ - public void findWebSuggest(String text) { + public void verifyWebSuggestIsPresent(String text) { ArrayList goldenGateResults = new ArrayList<>(mLauncher.waitForObjectsInContainer( mLauncher.waitForSystemLauncherObject(SEARCH_CONTAINER_RES_ID),