diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java index cc7b712842..810f4e3c59 100644 --- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java +++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java @@ -43,9 +43,13 @@ import android.util.ArrayMap; import android.util.Log; import androidx.annotation.MainThread; +import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.LauncherProvider; +import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.ItemInfo; @@ -74,6 +78,9 @@ public final class WellbeingModel { private static final int MSG_PACKAGE_REMOVED = 2; private static final int MSG_FULL_REFRESH = 3; + private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0; + private static final int IN_MINIMAL_DEVICE = 2; + // Welbeing contract private static final String PATH_ACTIONS = "actions"; private static final String PATH_MINIMAL_DEVICE = "minimal_device"; @@ -84,6 +91,8 @@ public final class WellbeingModel { private static final String EXTRA_MAX_NUM_ACTIONS_SHOWN = "max_num_actions_shown"; private static final String EXTRA_PACKAGES = "packages"; private static final String EXTRA_SUCCESS = "success"; + private static final String EXTRA_MINIMAL_DEVICE_STATE = "minimal_device_state"; + private static final String DB_NAME_MINIMAL_DEVICE = "minimal.db"; public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(WellbeingModel::new); @@ -121,11 +130,12 @@ public final class WellbeingModel { updateWellbeingData(); } else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) { // Wellbeing reports that minimal device state or config is changed. - updateLauncherModel(); + updateLauncherModel(context); } } }; - FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, this::updateLauncherModel); + FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, () -> + updateLauncherModel(context)); if (!TextUtils.isEmpty(mWellbeingProviderPkg)) { context.registerReceiver( @@ -170,7 +180,6 @@ public final class WellbeingModel { Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e); if (mIsInTest) throw new RuntimeException(e); } - updateWellbeingData(); } @@ -208,10 +217,34 @@ public final class WellbeingModel { mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH); } - private void updateLauncherModel() { - if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) return; + private void updateLauncherModel(@NonNull final Context context) { + if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) { + reloadLauncherInNormalMode(context); + return; + } + runWithMinimalDeviceConfigs((bundle) -> { + if (bundle.getInt(EXTRA_MINIMAL_DEVICE_STATE, UNKNOWN_MINIMAL_DEVICE_STATE) + == IN_MINIMAL_DEVICE) { + reloadLauncherInMinimalMode(context); + } else { + reloadLauncherInNormalMode(context); + } + }); + } - // TODO: init Launcher in minimal device / normal mode + private void reloadLauncherInNormalMode(@NonNull final Context context) { + LauncherSettings.Settings.call(context.getContentResolver(), + LauncherSettings.Settings.METHOD_SWITCH_DATABASE, + InvariantDeviceProfile.INSTANCE.get(context).dbFile); + } + + private void reloadLauncherInMinimalMode(@NonNull final Context context) { + final Bundle extras = new Bundle(); + extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY, + mWellbeingProviderPkg + ".api"); + LauncherSettings.Settings.call(context.getContentResolver(), + LauncherSettings.Settings.METHOD_SWITCH_DATABASE, + DB_NAME_MINIMAL_DEVICE, extras); } private Uri.Builder apiBuilder() { @@ -225,6 +258,9 @@ public final class WellbeingModel { */ @WorkerThread private void runWithMinimalDeviceConfigs(Consumer consumer) { + if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) { + return; + } if (DEBUG || mIsInTest) { Log.d(TAG, "runWithMinimalDeviceConfigs() called"); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index 1b8e244ac1..aad7e17df0 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -18,6 +18,7 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.graphics.OverviewScrim.SCRIM_MULTIPLIER; import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; @@ -70,6 +71,7 @@ public abstract class BaseRecentsViewStateController getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0); OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim(); SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher)); + SCRIM_MULTIPLIER.set(scrim, 1f); getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness()); } @@ -108,6 +110,8 @@ public abstract class BaseRecentsViewStateController OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim(); setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher), config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR)); + setter.setFloat(scrim, SCRIM_MULTIPLIER, 1f, + config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR)); setter.setFloat( mRecentsView, getTaskModalnessProperty(), diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index 57fd11a174..da3c4858a8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -66,8 +66,8 @@ public class NavBarToHomeTouchController implements TouchController, SingleAxisSwipeDetector.Listener { private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3; - // How much of the overview scrim we can remove during the transition. - private static final float OVERVIEW_TO_HOME_SCRIM_PROGRESS = 0.5f; + // The min amount of overview scrim we keep during the transition. + private static final float OVERVIEW_TO_HOME_SCRIM_MULTIPLIER = 0.5f; private final Launcher mLauncher; private final SingleAxisSwipeDetector mSwipeDetector; @@ -163,11 +163,11 @@ public class NavBarToHomeTouchController implements TouchController, RecentsView recentsView = mLauncher.getOverviewPanel(); AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher, builder); - float endScrimAlpha = Utilities.mapRange(OVERVIEW_TO_HOME_SCRIM_PROGRESS, - mStartState.getOverviewScrimAlpha(mLauncher), - mEndState.getOverviewScrimAlpha(mLauncher)); + builder.setFloat(mLauncher.getDragLayer().getOverviewScrim(), - OverviewScrim.SCRIM_PROGRESS, endScrimAlpha, PULLBACK_INTERPOLATOR); + OverviewScrim.SCRIM_MULTIPLIER, OVERVIEW_TO_HOME_SCRIM_MULTIPLIER, + PULLBACK_INTERPOLATOR); + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { builder.addOnFrameCallback(recentsView::redrawLiveTile); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index 3586b4f183..df6194d4b2 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -82,7 +82,15 @@ public abstract class TaskViewTouchController mDetector = new SingleAxisSwipeDetector(activity, this, dir); } - private boolean canInterceptTouch() { + private boolean canInterceptTouch(MotionEvent ev) { + if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0) { + // Don't intercept swipes on the nav bar, as user might be trying to go home + // during a task dismiss animation. + if (mCurrentAnimation != null) { + mCurrentAnimation.getAnimationPlayer().end(); + } + return false; + } if (mCurrentAnimation != null) { mCurrentAnimation.forceFinishIfCloseToEnd(); } @@ -118,7 +126,7 @@ public abstract class TaskViewTouchController clearState(); } if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mNoIntercept = !canInterceptTouch(); + mNoIntercept = !canInterceptTouch(ev); if (mNoIntercept) { return false; } diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index f0e78b9633..e08741d483 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -664,17 +664,17 @@ public class TouchInteractionService extends Service implements PluginListener 0f) { canvas.save(); diff --git a/res/layout/search_result_play_item.xml b/res/layout/search_result_play_item.xml new file mode 100644 index 0000000000..4e82eafc61 --- /dev/null +++ b/res/layout/search_result_play_item.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/search_section_title.xml b/res/layout/search_section_title.xml index c39a641d66..941901591c 100644 --- a/res/layout/search_section_title.xml +++ b/res/layout/search_section_title.xml @@ -13,11 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. --> - \ No newline at end of file + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index ef47eefba1..ad3e2b79f4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -70,6 +70,8 @@ Apps + + Try Now diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 53b65e812f..51a9dfe7ac 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -24,7 +24,6 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE; import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR; -import static com.android.launcher3.InstallShortcutReceiver.FLAG_DRAG_AND_DROP; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS; @@ -42,6 +41,9 @@ import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKG import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP; import static com.android.launcher3.logging.StatsLogManager.containerTypeToAtomState; +import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED; +import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP; +import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING; import static com.android.launcher3.popup.SystemShortcut.APP_INFO; import static com.android.launcher3.popup.SystemShortcut.INSTALL; import static com.android.launcher3.popup.SystemShortcut.WIDGETS; @@ -119,6 +121,7 @@ import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.model.BgDataModel.Callbacks; +import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.model.ModelUtils; import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.data.AppInfo; @@ -911,8 +914,8 @@ public class Launcher extends StatefulActivity implements Launche getUserEventDispatcher().startSession(); // Process any items that were added while Launcher was away. - InstallShortcutReceiver.disableAndFlushInstallQueue( - InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this); + ItemInstallQueue.INSTANCE.get(this) + .resumeModelPush(FLAG_ACTIVITY_PAUSED); // Refresh shortcuts if the permission changed. mModel.validateModelDataOnResume(); @@ -1006,7 +1009,7 @@ public class Launcher extends StatefulActivity implements Launche if (state == SPRING_LOADED) { // Prevent any Un/InstallShortcutReceivers from updating the db while we are // not on homescreen - InstallShortcutReceiver.enableInstallQueue(FLAG_DRAG_AND_DROP); + ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_DRAG_AND_DROP); getRotationHelper().setCurrentStateRequest(REQUEST_LOCK); mWorkspace.showPageIndicatorAtCurrentScroll(); @@ -1031,7 +1034,8 @@ public class Launcher extends StatefulActivity implements Launche if (state == NORMAL) { // Re-enable any Un/InstallShortcutReceiver and now process any queued items - InstallShortcutReceiver.disableAndFlushInstallQueue(FLAG_DRAG_AND_DROP, this); + ItemInstallQueue.INSTANCE.get(this) + .resumeModelPush(FLAG_DRAG_AND_DROP); // Clear any rotation locks when going to normal state getRotationHelper().setCurrentStateRequest(REQUEST_NONE); @@ -1065,7 +1069,7 @@ public class Launcher extends StatefulActivity implements Launche @Override protected void onPause() { // Ensure that items added to Launcher are queued until Launcher returns - InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED); + ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED); super.onPause(); mDragController.cancelDrag(); @@ -2420,8 +2424,8 @@ public class Launcher extends StatefulActivity implements Launche mPendingActivityResult = null; } - InstallShortcutReceiver.disableAndFlushInstallQueue( - InstallShortcutReceiver.FLAG_LOADER_RUNNING, this); + ItemInstallQueue.INSTANCE.get(this) + .resumeModelPush(FLAG_LOADER_RUNNING); // When undoing the removal of the last item on a page, return to that page. // Since we are just resetting the current page without user interaction, @@ -2448,8 +2452,8 @@ public class Launcher extends StatefulActivity implements Launche private ValueAnimator createNewAppBounceAnimation(View v, int i) { ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v) - .setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION); - bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY); + .setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION); + bounceAnim.setStartDelay(i * ItemInstallQueue.NEW_SHORTCUT_STAGGER_DELAY); bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION)); return bounceAnim; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f58e0b496a..c51a84e5ed 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -43,6 +43,7 @@ import com.android.launcher3.model.BaseModelUpdateTask; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.CacheDataUpdatedTask; +import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.model.LoaderResults; import com.android.launcher3.model.LoaderTask; import com.android.launcher3.model.ModelDelegate; @@ -328,7 +329,8 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi */ public boolean startLoader() { // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems - InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING); + ItemInstallQueue.INSTANCE.get(mApp.getContext()) + .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING); synchronized (mLock) { // Don't bother to start the thread if we know it's not going to do anything final Callbacks[] callbacksList = getCallbacks(); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index fdbbf4e0d8..2973cf7278 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -100,10 +100,12 @@ public class LauncherProvider extends ContentProvider { public static final int SCHEMA_VERSION = 28; public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings"; + public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY"; static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; protected DatabaseHelper mOpenHelper; + protected String mProviderAuthority; private long mLastRestoreTimestamp = 0L; @@ -367,7 +369,8 @@ public class LauncherProvider extends ContentProvider { case LauncherSettings.Settings.METHOD_WAS_EMPTY_DB_CREATED : { Bundle result = new Bundle(); result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, - Utilities.getPrefs(getContext()).getBoolean(EMPTY_DATABASE_CREATED, false)); + Utilities.getPrefs(getContext()).getBoolean( + mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)); return result; } case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: { @@ -437,6 +440,7 @@ public class LauncherProvider extends ContentProvider { getContext(), true /* forMigration */))); return result; } + return null; } case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: { if (MULTI_DB_GRID_MIRATION_ALGO.get()) { @@ -450,6 +454,23 @@ public class LauncherProvider extends ContentProvider { () -> mOpenHelper)); return result; } + return null; + } + case LauncherSettings.Settings.METHOD_SWITCH_DATABASE: { + if (TextUtils.equals(arg, mOpenHelper.getDatabaseName())) return null; + final DatabaseHelper helper = mOpenHelper; + if (extras == null || !extras.containsKey(KEY_LAYOUT_PROVIDER_AUTHORITY)) { + mProviderAuthority = null; + } else { + mProviderAuthority = extras.getString(KEY_LAYOUT_PROVIDER_AUTHORITY); + } + mOpenHelper = DatabaseHelper.createDatabaseHelper( + getContext(), arg, false /* forMigration */); + helper.close(); + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app == null) return null; + app.getModel().forceReload(); + return null; } } return null; @@ -492,7 +513,8 @@ public class LauncherProvider extends ContentProvider { } private void clearFlagEmptyDbCreated() { - Utilities.getPrefs(getContext()).edit().remove(EMPTY_DATABASE_CREATED).commit(); + Utilities.getPrefs(getContext()).edit() + .remove(mOpenHelper.getKey(EMPTY_DATABASE_CREATED)).commit(); } /** @@ -505,7 +527,7 @@ public class LauncherProvider extends ContentProvider { synchronized private void loadDefaultFavoritesIfNecessary() { SharedPreferences sp = Utilities.getPrefs(getContext()); - if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) { + if (sp.getBoolean(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) { Log.d(TAG, "loading default workspace"); AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost(); @@ -553,8 +575,13 @@ public class LauncherProvider extends ContentProvider { */ private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) { Context ctx = getContext(); - String authority = Settings.Secure.getString(ctx.getContentResolver(), - "launcher3.layout.provider"); + final String authority; + if (!TextUtils.isEmpty(mProviderAuthority)) { + authority = mProviderAuthority; + } else { + authority = Settings.Secure.getString(ctx.getContentResolver(), + "launcher3.layout.provider"); + } if (TextUtils.isEmpty(authority)) { return null; } @@ -693,12 +720,26 @@ public class LauncherProvider extends ContentProvider { } } + /** + * Re-composite given key in respect to database. If the current db is + * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to + * given key. e.g. consider key="EMPTY_DATABASE_CREATED", dbName="minimal.db", the returning + * string will be "EMPTY_DATABASE_CREATED@minimal.db". + */ + String getKey(final String key) { + if (TextUtils.equals(getDatabaseName(), LauncherFiles.LAUNCHER_DB)) { + return key; + } + return key + "@" + getDatabaseName(); + } + /** * Overriden in tests. */ protected void onEmptyDbCreated() { // Set the flag for empty DB - Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit(); + Utilities.getPrefs(mContext).edit().putBoolean(getKey(EMPTY_DATABASE_CREATED), true) + .commit(); } public long getSerialNumberForUser(UserHandle user) { diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 5512654390..58a418edec 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -354,14 +354,20 @@ public class LauncherSettings { public static final String METHOD_PREP_FOR_PREVIEW = "prep_for_preview"; + public static final String METHOD_SWITCH_DATABASE = "switch_database"; + public static final String EXTRA_VALUE = "value"; public static Bundle call(ContentResolver cr, String method) { - return call(cr, method, null); + return call(cr, method, null /* arg */); } public static Bundle call(ContentResolver cr, String method, String arg) { - return cr.call(CONTENT_URI, method, arg, null); + return call(cr, method, arg, null /* extras */); + } + + public static Bundle call(ContentResolver cr, String method, String arg, Bundle extras) { + return cr.call(CONTENT_URI, method, arg, extras); } } } diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java index e48ffb95ea..007e5f5370 100644 --- a/src/com/android/launcher3/SessionCommitReceiver.java +++ b/src/com/android/launcher3/SessionCommitReceiver.java @@ -25,6 +25,7 @@ import android.content.pm.PackageManager; import android.os.UserHandle; import android.text.TextUtils; +import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.pm.InstallSessionHelper; /** @@ -59,7 +60,8 @@ public class SessionCommitReceiver extends BroadcastReceiver { return; } - InstallShortcutReceiver.queueApplication(info.getAppPackageName(), user, context); + ItemInstallQueue.INSTANCE.get(context) + .queueItem(info.getAppPackageName(), user); } public static boolean isEnabled(Context context) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 3be9ac7e07..6bfd3495e5 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -22,6 +22,7 @@ import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE; import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED; import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_INACCESSIBLE; +import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.SPRING_LOADED; @@ -943,7 +944,10 @@ public class Workspace extends PagedView super.onScrollChanged(l, t, oldl, oldt); // Update the page indicator progress. - boolean isTransitioning = mIsSwitchingState + // Unlike from other states, we show the page indicator when transitioning from HINT_STATE. + boolean isSwitchingState = mIsSwitchingState + && mLauncher.getStateManager().getCurrentStableState() != HINT_STATE; + boolean isTransitioning = isSwitchingState || (getLayoutTransition() != null && getLayoutTransition().isRunning()); if (!isTransitioning) { showPageIndicatorAtCurrentScroll(); diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index af3722a1f8..99fff4d095 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; +import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; @@ -527,6 +529,25 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo return mViewPager == null ? getActiveRecyclerView() : mViewPager; } + /** + * Handles selection on focused view and returns success + */ + public boolean selectFocusedView(View v) { + ItemInfo itemInfo = getHighlightedItemInfo(); + if (itemInfo != null) { + return mLauncher.startActivitySafely(v, itemInfo.getIntent(), itemInfo); + } + AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild(); + if (focusedItem instanceof AdapterItemWithPayload) { + Runnable onSelection = ((AdapterItemWithPayload) focusedItem).getSelectionHandler(); + if (onSelection != null) { + onSelection.run(); + return true; + } + } + return false; + } + /** * Returns the ItemInfo of a view that is in focus, ready to be launched by an IME. */ diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 2cec797fd0..c61f01f206 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -40,10 +40,10 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; -import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem; +import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler; +import com.android.launcher3.allapps.search.SearchSectionInfo; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.util.PackageManagerHelper; -import com.android.launcher3.views.HeroSearchResultView; import java.util.List; @@ -71,6 +71,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter Play load Type + */ + public static class AdapterItemWithPayload extends AdapterItem { + private T mPayload; + private Runnable mSelectionHandler; + + public AdapterItemWithPayload(T payload, int type) { + mPayload = payload; + viewType = type; + } + + public void setSelectionHandler(Runnable runnable) { + mSelectionHandler = runnable; + } + + public Runnable getSelectionHandler() { + return mSelectionHandler; + } + + public T getPayload() { + return mPayload; + } + } + /** * A subclass of GridLayoutManager that overrides accessibility values during app search. */ @@ -286,6 +390,9 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter items = mApps.getAdapterItems(); + List items = mApps.getAdapterItems(); // Skip early if there are no items or we haven't been measured if (items.isEmpty() || mNumAppsPerRow == 0) { @@ -352,7 +352,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine @Override public int getCurrentScrollY() { // Return early if there are no items or we haven't been measured - List items = mApps.getAdapterItems(); + List items = mApps.getAdapterItems(); if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) { return -1; } @@ -368,14 +368,14 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine } public int getCurrentScrollY(int position, int offset) { - List items = mApps.getAdapterItems(); - AlphabeticalAppsList.AdapterItem posItem = position < items.size() ? - items.get(position) : null; + List items = mApps.getAdapterItems(); + AllAppsGridAdapter.AdapterItem posItem = position < items.size() + ? items.get(position) : null; int y = mCachedScrollPositions.get(position, -1); if (y < 0) { y = 0; for (int i = 0; i < position; i++) { - AlphabeticalAppsList.AdapterItem item = items.get(i); + AllAppsGridAdapter.AdapterItem item = items.get(i); if (AllAppsGridAdapter.isIconViewType(item.viewType)) { // Break once we reach the desired row if (posItem != null && posItem.viewType == item.viewType && diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java index a168c06d72..6f29e1102c 100644 --- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java +++ b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java @@ -47,13 +47,13 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration { // Since views in the same section will follow each other, we can skip to a last view in // a section to get the bounds of the section without having to iterate on every item. int itemCount = parent.getChildCount(); - List adapterItems = mAppsView.getApps().getAdapterItems(); + List adapterItems = mAppsView.getApps().getAdapterItems(); SectionDecorationHandler lastDecorationHandler = null; int i = 0; while (i < itemCount) { View view = parent.getChildAt(i); int position = parent.getChildAdapterPosition(view); - AlphabeticalAppsList.AdapterItem adapterItem = adapterItems.get(position); + AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position); if (adapterItem.searchSectionInfo != null) { SearchSectionInfo sectionInfo = adapterItem.searchSectionInfo; int endIndex = Math.min(i + sectionInfo.getPosEnd() - position, itemCount - 1); diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 7379dbed9a..8c059d5fce 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -19,10 +19,10 @@ package com.android.launcher3.allapps; import android.content.Context; import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; import com.android.launcher3.allapps.search.SearchSectionInfo; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.LabelComparator; @@ -62,101 +62,6 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { } } - /** - * Info about a particular adapter item (can be either section or app) - */ - public static class AdapterItem { - /** Common properties */ - // The index of this adapter item in the list - public int position; - // The type of this item - public int viewType; - - /** App-only properties */ - // The section name of this app. Note that there can be multiple items with different - // sectionNames in the same section - public String sectionName = null; - // The row that this item shows up on - public int rowIndex; - // The index of this app in the row - public int rowAppIndex; - // The associated AppInfo for the app - public AppInfo appInfo = null; - // The index of this app not including sections - public int appIndex = -1; - // Search section associated to result - public SearchSectionInfo searchSectionInfo = null; - - public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo, - int appIndex) { - AdapterItem item = new AdapterItem(); - item.viewType = AllAppsGridAdapter.VIEW_TYPE_ICON; - item.position = pos; - item.sectionName = sectionName; - item.appInfo = appInfo; - item.appIndex = appIndex; - return item; - } - - public static AdapterItem asEmptySearch(int pos) { - AdapterItem item = new AdapterItem(); - item.viewType = AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH; - item.position = pos; - return item; - } - - public static AdapterItem asAllAppsDivider(int pos) { - AdapterItem item = new AdapterItem(); - item.viewType = AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER; - item.position = pos; - return item; - } - - public static AdapterItem asMarketSearch(int pos) { - AdapterItem item = new AdapterItem(); - item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET; - item.position = pos; - return item; - } - - /** - * Factory method for search section title AdapterItem - */ - public static AdapterItem asSearchTitle(SearchSectionInfo sectionInfo, int pos) { - AdapterItem item = new AdapterItem(); - item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_CORPUS_TITLE; - item.position = pos; - item.searchSectionInfo = sectionInfo; - return item; - } - - boolean isCountedForAccessibility() { - return viewType == AllAppsGridAdapter.VIEW_TYPE_ICON - || viewType == AllAppsGridAdapter.VIEW_TYPE_SEARCH_HERO_APP; - } - } - - /** - * Extension of AdapterItem that contains shortcut workspace items - */ - public static class HeroAppAdapterItem extends AdapterItem { - private ArrayList mShortcutInfos; - - public HeroAppAdapterItem(AppInfo info, ArrayList shortcutInfos) { - viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_HERO_APP; - mShortcutInfos = shortcutInfos; - appInfo = info; - } - - /** - * Returns list of shortcuts for appInfo - */ - public ArrayList getShortcutInfos() { - return mShortcutInfos; - } - - } - private final BaseDraggingActivity mLauncher; @@ -396,7 +301,9 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { adapterItem.position = i; mAdapterItems.add(adapterItem); if (adapterItem.searchSectionInfo != lastSection) { - adapterItem.searchSectionInfo.setPosStart(i); + if (adapterItem.searchSectionInfo != null) { + adapterItem.searchSectionInfo.setPosStart(i); + } if (lastSection != null) { lastSection.setPosEnd(i - 1); } diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java index 0d8748165a..2e5ed3ec35 100644 --- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java @@ -15,35 +15,26 @@ */ package com.android.launcher3.allapps.search; -import android.content.Context; -import android.content.pm.PackageManager; import android.os.Bundle; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; -import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.View.OnFocusChangeListener; import android.view.inputmethod.EditorInfo; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; -import android.widget.Toast; - -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; -import com.android.launcher3.allapps.AlphabeticalAppsList; +import com.android.launcher3.allapps.AllAppsGridAdapter; +import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.PackageManagerHelper; import com.android.systemui.plugins.AllAppsSearchPlugin; -import com.android.systemui.plugins.PluginListener; import java.util.ArrayList; import java.util.List; @@ -54,17 +45,14 @@ import java.util.function.Consumer; */ public class AllAppsSearchBarController implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener, - OnFocusChangeListener, PluginListener { + OnFocusChangeListener { - private static final String TAG = "AllAppsSearchBarContoller"; protected BaseDraggingActivity mLauncher; protected Callbacks mCb; protected ExtendedEditText mInput; protected String mQuery; protected SearchAlgorithm mSearchAlgorithm; - private AllAppsSearchPlugin mPlugin; - private Consumer mPlubinCb; public void setVisibility(int visibility) { mInput.setVisibility(visibility); @@ -85,18 +73,13 @@ public class AllAppsSearchBarController mInput.setOnBackKeyListener(this); mInput.setOnFocusChangeListener(this); mSearchAlgorithm = searchAlgorithm; - - PluginManagerWrapper.INSTANCE.get(launcher).addPluginListener(this, - AllAppsSearchPlugin.class); - mPlubinCb = secondaryCb; } @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - if (mPlugin != null) { - if (s.length() == 0) { - mPlugin.startedTyping(); - } + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + if (mSearchAlgorithm instanceof PluginWrapper) { + ((PluginWrapper) mSearchAlgorithm).runOnPluginIfConnected( + AllAppsSearchPlugin::startedTyping); } } @@ -114,9 +97,6 @@ public class AllAppsSearchBarController } else { mSearchAlgorithm.cancel(false); mSearchAlgorithm.doSearch(mQuery, mCb); - if (mPlugin != null) { - mPlugin.performSearch(mQuery, mPlubinCb); - } } } @@ -133,10 +113,8 @@ public class AllAppsSearchBarController public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { if (actionId == EditorInfo.IME_ACTION_SEARCH) { - ItemInfo info = Launcher.getLauncher(mLauncher).getAppsView() - .getHighlightedItemInfo(); - if (info != null) { - return mLauncher.startActivitySafely(v, info.getIntent(), info); + if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) { + return true; } } } @@ -197,43 +175,14 @@ public class AllAppsSearchBarController return mInput.isFocused(); } - @Override - public void onPluginConnected(AllAppsSearchPlugin allAppsSearchPlugin, Context context) { - if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { - mPlugin = allAppsSearchPlugin; - checkCallPermission(); - } - } - /** - * Check call permissions. + * A wrapper setup for running essential calls to plugin from search controller */ - public void checkCallPermission() { - final String[] permission = {"android.permission.CALL_PHONE", - "android.permission.READ_CONTACTS"}; - boolean request = false; - for (String p : permission) { - int permissionCheck = ContextCompat.checkSelfPermission(mLauncher, p); - if (permissionCheck != PackageManager.PERMISSION_GRANTED) { - request = true; - } - } - - if (!request) return; - boolean rationale = false; - for (String p : permission) { - if (mLauncher.shouldShowRequestPermissionRationale(p)) { - rationale = true; - } - if (rationale) { - Log.e(TAG, p + " Show rationale"); - Toast.makeText(mLauncher, "Requesting Permissions", Toast.LENGTH_SHORT).show(); - } else { - ActivityCompat.requestPermissions(mLauncher, permission, 123); - Log.e(TAG, p + " request permission"); - } - } - + public interface PluginWrapper { + /** + * executes call if plugin is connected + */ + void runOnPluginIfConnected(Consumer plugin); } /** @@ -246,11 +195,23 @@ public class AllAppsSearchBarController * * @param items sorted list of search result adapter items. */ - void onSearchResult(String query, ArrayList items); + void onSearchResult(String query, ArrayList items); /** * Called when the search results should be cleared. */ void clearSearchResult(); } + + /** + * An interface for supporting dynamic search results + * + * @param Type of payload + */ + public interface PayloadResultHandler { + /** + * Updates View using Adapter's payload + */ + void applyAdapterInfo(AdapterItemWithPayload adapterItemWithPayload); + } } \ No newline at end of file diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index e8a0d7a493..6f183eebc0 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -42,6 +42,7 @@ import com.android.launcher3.Insettable; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.AlphabeticalAppsList; import com.android.launcher3.allapps.SearchUiManager; @@ -172,7 +173,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText } @Override - public void onSearchResult(String query, ArrayList items) { + public void onSearchResult(String query, ArrayList items) { if (items != null) { mApps.setSearchResults(items); notifyResultChanged(); diff --git a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java index e67e8972e7..fb3d953bac 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java @@ -16,6 +16,7 @@ package com.android.launcher3.allapps.search; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS; +import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_SEARCH_HERO_APP; import android.content.Context; import android.content.pm.ShortcutInfo; @@ -23,9 +24,9 @@ import android.content.pm.ShortcutInfo; import androidx.annotation.WorkerThread; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.allapps.AllAppsGridAdapter; +import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler; -import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem; -import com.android.launcher3.allapps.AlphabeticalAppsList.HeroAppAdapterItem; import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.AllAppsList; import com.android.launcher3.model.BaseModelUpdateTask; @@ -82,7 +83,7 @@ public class AppsSearchPipeline implements SearchPipeline { /** * Returns MAX_SHORTCUTS_COUNT shortcuts from local cache - * TODO: Shortcuts should be ranked based on relevancy + * TODO: Shortcuts should be ranked based on relevancy */ private ArrayList getShortcutInfos(Context context, AppInfo appInfo) { List shortcuts = new ShortcutRequest(context, appInfo.user) @@ -126,7 +127,9 @@ public class AppsSearchPipeline implements SearchPipeline { //hero app AppInfo appInfo = apps.get(i); ArrayList shortcuts = getShortcutInfos(context, appInfo); - AdapterItem adapterItem = new HeroAppAdapterItem(appInfo, shortcuts); + AdapterItem adapterItem = new AllAppsGridAdapter.AdapterItemWithPayload(shortcuts, + VIEW_TYPE_SEARCH_HERO_APP); + adapterItem.appInfo = appInfo; adapterItem.searchSectionInfo = mSearchSectionInfo; adapterItems.add(adapterItem); } diff --git a/src/com/android/launcher3/allapps/search/SearchPipeline.java b/src/com/android/launcher3/allapps/search/SearchPipeline.java index 321674025a..545f0e3fa2 100644 --- a/src/com/android/launcher3/allapps/search/SearchPipeline.java +++ b/src/com/android/launcher3/allapps/search/SearchPipeline.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.allapps.search; -import com.android.launcher3.allapps.AlphabeticalAppsList; +import com.android.launcher3.allapps.AllAppsGridAdapter; import java.util.ArrayList; import java.util.function.Consumer; @@ -28,5 +28,5 @@ public interface SearchPipeline { /** * Perform query */ - void performSearch(String query, Consumer> cb); + void performSearch(String query, Consumer> cb); } diff --git a/src/com/android/launcher3/allapps/search/SearchSectionInfo.java b/src/com/android/launcher3/allapps/search/SearchSectionInfo.java index dee0ffd2df..e026e8449d 100644 --- a/src/com/android/launcher3/allapps/search/SearchSectionInfo.java +++ b/src/com/android/launcher3/allapps/search/SearchSectionInfo.java @@ -15,8 +15,6 @@ */ package com.android.launcher3.allapps.search; -import android.content.Context; - import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler; /** @@ -24,7 +22,7 @@ import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHa */ public class SearchSectionInfo { - private final int mTitleResId; + private String mTitle; private SectionDecorationHandler mDecorationHandler; public int getPosStart() { @@ -47,11 +45,11 @@ public class SearchSectionInfo { private int mPosEnd; public SearchSectionInfo() { - this(-1); + this(null); } - public SearchSectionInfo(int titleResId) { - mTitleResId = titleResId; + public SearchSectionInfo(String title) { + mTitle = title; } public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) { @@ -66,10 +64,7 @@ public class SearchSectionInfo { /** * Returns the section's title */ - public String getTitle(Context context) { - if (mTitleResId == -1) { - return ""; - } - return context.getString(mTitleResId); + public String getTitle() { + return mTitle == null ? "" : mTitle; } } diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java index 0df67133ab..2d625c5373 100644 --- a/src/com/android/launcher3/dragndrop/AddItemActivity.java +++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java @@ -43,13 +43,13 @@ import android.view.View.OnLongClickListener; import android.view.View.OnTouchListener; import com.android.launcher3.BaseActivity; -import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetHost; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; +import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.pm.PinRequestHelper; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; @@ -249,7 +249,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener */ public void onPlaceAutomaticallyClick(View v) { if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) { - InstallShortcutReceiver.queueShortcut(mRequest.getShortcutInfo(), this); + ItemInstallQueue.INSTANCE.get(this).queueItem(mRequest.getShortcutInfo()); logCommand(Action.Command.CONFIRM); mRequest.accept(); finish(); @@ -270,7 +270,8 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener } private void acceptWidget(int widgetId) { - InstallShortcutReceiver.queueWidget(mRequest.getAppWidgetProviderInfo(this), widgetId, this); + ItemInstallQueue.INSTANCE.get(this) + .queueItem(mRequest.getAppWidgetProviderInfo(this), widgetId); mWidgetOptions.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); mRequest.accept(mWidgetOptions); logCommand(Action.Command.CONFIRM); diff --git a/src/com/android/launcher3/graphics/OverviewScrim.java b/src/com/android/launcher3/graphics/OverviewScrim.java index 94acbfdcca..c0c3e5e9c2 100644 --- a/src/com/android/launcher3/graphics/OverviewScrim.java +++ b/src/com/android/launcher3/graphics/OverviewScrim.java @@ -22,6 +22,7 @@ import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.OVERVIEW; import android.graphics.Rect; +import android.util.FloatProperty; import android.view.View; import android.view.ViewGroup; @@ -33,10 +34,25 @@ import androidx.annotation.Nullable; */ public class OverviewScrim extends Scrim { + public static final FloatProperty SCRIM_MULTIPLIER = + new FloatProperty("scrimMultiplier") { + @Override + public Float get(OverviewScrim scrim) { + return scrim.mScrimMultiplier; + } + + @Override + public void setValue(OverviewScrim scrim, float v) { + scrim.setScrimMultiplier(v); + } + }; + private @NonNull View mStableScrimmedView; // Might be higher up if mStableScrimmedView is invisible. private @Nullable View mCurrentScrimmedView; + private float mScrimMultiplier = 1f; + public OverviewScrim(View view) { super(view); mStableScrimmedView = mCurrentScrimmedView = mLauncher.getOverviewPanel(); @@ -68,4 +84,16 @@ public class OverviewScrim extends Scrim { public @Nullable View getScrimmedView() { return mCurrentScrimmedView; } + + private void setScrimMultiplier(float scrimMultiplier) { + if (Float.compare(mScrimMultiplier, scrimMultiplier) != 0) { + mScrimMultiplier = scrimMultiplier; + invalidate(); + } + } + + @Override + protected int getScrimAlpha() { + return Math.round(super.getScrimAlpha() * mScrimMultiplier); + } } diff --git a/src/com/android/launcher3/graphics/Scrim.java b/src/com/android/launcher3/graphics/Scrim.java index f90962d5d1..a151cba47d 100644 --- a/src/com/android/launcher3/graphics/Scrim.java +++ b/src/com/android/launcher3/graphics/Scrim.java @@ -61,7 +61,11 @@ public class Scrim implements View.OnAttachStateChangeListener, } public void draw(Canvas canvas) { - canvas.drawColor(setColorAlphaBound(mScrimColor, mScrimAlpha)); + canvas.drawColor(setColorAlphaBound(mScrimColor, getScrimAlpha())); + } + + protected int getScrimAlpha() { + return mScrimAlpha; } private void setScrimProgress(float progress) { diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index 140342f98c..7e45021bf3 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -31,7 +31,6 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; -import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Workspace; @@ -298,7 +297,7 @@ public class BgDataModel { .filter(wi -> wi.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) .map(ShortcutKey::fromItemInfo), // Pending shortcuts - InstallShortcutReceiver.getPendingShortcuts(context) + ItemInstallQueue.INSTANCE.get(context).getPendingShortcuts() .stream().filter(si -> si.user.equals(user))) .collect(groupingBy(ShortcutKey::getPackageName, mapping(ShortcutKey::getId, Collectors.toSet()))); diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/model/ItemInstallQueue.java similarity index 81% rename from src/com/android/launcher3/InstallShortcutReceiver.java rename to src/com/android/launcher3/model/ItemInstallQueue.java index d2b05c5b01..6238db3152 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/model/ItemInstallQueue.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.model; import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID; @@ -42,13 +42,19 @@ import android.util.Pair; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.Utilities; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.UserCache; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; +import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; import org.json.JSONException; @@ -63,16 +69,15 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -public class InstallShortcutReceiver { +/** + * Class to maintain a queue of pending items to be added to the workspace. + */ +public class ItemInstallQueue { public static final int FLAG_ACTIVITY_PAUSED = 1; public static final int FLAG_LOADER_RUNNING = 2; public static final int FLAG_DRAG_AND_DROP = 4; - // Determines whether to defer installing shortcuts immediately until - // processAllPendingInstalls() is called. - private static int sInstallQueueDisabledFlags = 0; - private static final String TAG = "InstallShortcutReceiver"; private static final boolean DBG = false; @@ -82,10 +87,23 @@ public class InstallShortcutReceiver { public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450; public static final int NEW_SHORTCUT_STAGGER_DELAY = 85; + public static MainThreadInitializedObject INSTANCE = + new MainThreadInitializedObject<>(ItemInstallQueue::new); + + private final Context mContext; + + // Determines whether to defer installing shortcuts immediately until + // processAllPendingInstalls() is called. + private int mInstallQueueDisabledFlags = 0; + + private ItemInstallQueue(Context context) { + mContext = context; + } + @WorkerThread - private static void addToQueue(Context context, PendingInstallShortcutInfo info) { - String encoded = info.encodeToString(context); - SharedPreferences prefs = Utilities.getPrefs(context); + private void addToQueue(PendingInstallShortcutInfo info) { + String encoded = info.encodeToString(mContext); + SharedPreferences prefs = Utilities.getPrefs(mContext); Set strings = prefs.getStringSet(APPS_PENDING_INSTALL, null); strings = (strings != null) ? new HashSet<>(strings) : new HashSet<>(1); strings.add(encoded); @@ -93,14 +111,15 @@ public class InstallShortcutReceiver { } @WorkerThread - private static void flushQueueInBackground(Context context) { - if (Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null) { + private void flushQueueInBackground() { + Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity(); + if (launcher == null) { // Launcher not loaded return; } ArrayList> installQueue = new ArrayList<>(); - SharedPreferences prefs = Utilities.getPrefs(context); + SharedPreferences prefs = Utilities.getPrefs(mContext); Set strings = prefs.getStringSet(APPS_PENDING_INSTALL, null); if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings); if (strings == null) { @@ -108,29 +127,31 @@ public class InstallShortcutReceiver { } for (String encoded : strings) { - PendingInstallShortcutInfo info = decode(encoded, context); + PendingInstallShortcutInfo info = decode(encoded, mContext); if (info == null) { continue; } // Generate a shortcut info to add into the model - installQueue.add(info.getItemInfo(context)); + installQueue.add(info.getItemInfo(mContext)); } prefs.edit().remove(APPS_PENDING_INSTALL).apply(); if (!installQueue.isEmpty()) { - LauncherAppState.getInstance(context).getModel() - .addAndBindAddedWorkspaceItems(installQueue); + launcher.getModel().addAndBindAddedWorkspaceItems(installQueue); } } - public static void removeFromInstallQueue(Context context, HashSet packageNames, - UserHandle user) { + /** + * Removes previously added items from the queue. + */ + @WorkerThread + public void removeFromInstallQueue(HashSet packageNames, UserHandle user) { if (packageNames.isEmpty()) { return; } Preconditions.assertWorkerThread(); - SharedPreferences sp = Utilities.getPrefs(context); + SharedPreferences sp = Utilities.getPrefs(mContext); Set strings = sp.getStringSet(APPS_PENDING_INSTALL, null); if (DBG) { Log.d(TAG, "APPS_PENDING_INSTALL: " + strings @@ -144,7 +165,7 @@ public class InstallShortcutReceiver { while (newStringsIter.hasNext()) { String encoded = newStringsIter.next(); try { - Decoder decoder = new Decoder(encoded, context); + Decoder decoder = new Decoder(encoded, mContext); if (packageNames.contains(getIntentPackage(decoder.intent)) && user.equals(decoder.user)) { newStringsIter.remove(); @@ -157,30 +178,42 @@ public class InstallShortcutReceiver { sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply(); } - public static void queueShortcut(ShortcutInfo info, Context context) { - queuePendingShortcutInfo(new PendingInstallShortcutInfo(info), context); + /** + * Adds an item to the install queue + */ + public void queueItem(ShortcutInfo info) { + queuePendingShortcutInfo(new PendingInstallShortcutInfo(info)); } - public static void queueWidget(AppWidgetProviderInfo info, int widgetId, Context context) { - queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId), context); + /** + * Adds an item to the install queue + */ + public void queueItem(AppWidgetProviderInfo info, int widgetId) { + queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId)); } - public static void queueApplication( - String packageName, UserHandle userHandle, Context context) { - queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle), context); + /** + * Adds an item to the install queue + */ + public void queueItem(String packageName, UserHandle userHandle) { + queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle)); } - public static HashSet getPendingShortcuts(Context context) { + /** + * Returns all pending shorts in the queue + */ + @WorkerThread + public HashSet getPendingShortcuts() { HashSet result = new HashSet<>(); - Set strings = Utilities.getPrefs(context).getStringSet(APPS_PENDING_INSTALL, null); + Set strings = Utilities.getPrefs(mContext).getStringSet(APPS_PENDING_INSTALL, null); if (strings == null || ((Collection) strings).isEmpty()) { return result; } for (String encoded : strings) { try { - Decoder decoder = new Decoder(encoded, context); + Decoder decoder = new Decoder(encoded, mContext); if (decoder.optInt(Favorites.ITEM_TYPE, -1) == ITEM_TYPE_DEEP_SHORTCUT) { result.add(ShortcutKey.fromIntent(decoder.intent, decoder.user)); } @@ -191,28 +224,35 @@ public class InstallShortcutReceiver { return result; } - private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) { + private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) { // Queue the item up for adding if launcher has not loaded properly yet - MODEL_EXECUTOR.post(() -> addToQueue(context, info)); - flushInstallQueue(context); + MODEL_EXECUTOR.post(() -> addToQueue(info)); + flushInstallQueue(); } - public static void enableInstallQueue(int flag) { - sInstallQueueDisabledFlags |= flag; - } - public static void disableAndFlushInstallQueue(int flag, Context context) { - sInstallQueueDisabledFlags &= ~flag; - flushInstallQueue(context); + /** + * Pauses the push-to-model flow until unpaused. All items are held in the queue and + * not added to the model. + */ + public void pauseModelPush(int flag) { + mInstallQueueDisabledFlags |= flag; } - static void flushInstallQueue(Context context) { - if (sInstallQueueDisabledFlags != 0) { + /** + * Adds all the queue items to the model if the use is completely resumed. + */ + public void resumeModelPush(int flag) { + mInstallQueueDisabledFlags &= ~flag; + flushInstallQueue(); + } + + private void flushInstallQueue() { + if (mInstallQueueDisabledFlags != 0) { return; } - MODEL_EXECUTOR.post(() -> flushQueueInBackground(context)); + MODEL_EXECUTOR.post(this::flushQueueInBackground); } - private static class PendingInstallShortcutInfo extends ItemInfo { final Intent intent; diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index c0ae6f903c..896bfb666e 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -28,7 +28,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.Log; -import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.config.FeatureFlags; @@ -320,7 +319,8 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { deleteAndBindComponentsRemoved(removeMatch); // Remove any queued items from the install queue - InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser); + ItemInstallQueue.INSTANCE.get(context) + .removeFromInstallQueue(removedPackages, mUser); } if (mOp == OP_ADD) { diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java index d546013fca..753a6dd85e 100644 --- a/src/com/android/launcher3/pm/InstallSessionHelper.java +++ b/src/com/android/launcher3/pm/InstallSessionHelper.java @@ -32,11 +32,11 @@ import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; -import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.LauncherSettings; import com.android.launcher3.SessionCommitReceiver; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.LooperExecutor; @@ -213,8 +213,8 @@ public class InstallSessionHelper { && !mPromiseIconIds.contains(sessionInfo.getSessionId()) && new PackageManagerHelper(mAppContext).getApplicationInfo( sessionInfo.getAppPackageName(), getUserHandle(sessionInfo), 0) == null) { - InstallShortcutReceiver.queueApplication( - sessionInfo.getAppPackageName(), getUserHandle(sessionInfo), mAppContext); + ItemInstallQueue.INSTANCE.get(mAppContext) + .queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo)); mPromiseIconIds.add(sessionInfo.getSessionId()); updatePromiseIconPrefs(); diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java index c2a02bcdab..10f3c41f35 100644 --- a/src/com/android/launcher3/views/HeroSearchResultView.java +++ b/src/com/android/launcher3/views/HeroSearchResultView.java @@ -30,7 +30,8 @@ import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; import com.android.launcher3.Launcher; import com.android.launcher3.R; -import com.android.launcher3.allapps.AlphabeticalAppsList; +import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; +import com.android.launcher3.allapps.search.AllAppsSearchBarController; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.graphics.DragPreviewProvider; @@ -47,7 +48,8 @@ import java.util.List; /** * A view representing a high confidence app search result that includes shortcuts */ -public class HeroSearchResultView extends LinearLayout implements DragSource { +public class HeroSearchResultView extends LinearLayout implements DragSource, + AllAppsSearchBarController.PayloadResultHandler> { BubbleTextView mBubbleTextView; View mIconView; @@ -96,18 +98,18 @@ public class HeroSearchResultView extends LinearLayout implements DragSource { /** * Apply {@link ItemInfo} for appIcon and shortcut Icons */ - public void prepareUsingAdapterItem(AlphabeticalAppsList.HeroAppAdapterItem adapterItem) { + @Override + public void applyAdapterInfo(AdapterItemWithPayload> adapterItem) { mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo); mIconView.setBackground(mBubbleTextView.getIcon()); mIconView.setTag(adapterItem.appInfo); - List shorcutInfos = adapterItem.getShortcutInfos(); + List shorcutInfos = adapterItem.getPayload(); for (int i = 0; i < mDeepShortcutTextViews.length; i++) { mDeepShortcutTextViews[i].setVisibility(shorcutInfos.size() > i ? VISIBLE : GONE); if (i < shorcutInfos.size()) { mDeepShortcutTextViews[i].applyFromWorkspaceItem(shorcutInfos.get(i)); } } - } @Override @@ -126,7 +128,6 @@ public class HeroSearchResultView extends LinearLayout implements DragSource { mIconView.setVisibility(willDraw ? View.VISIBLE : View.INVISIBLE); } - /** * Drag and drop handler for popup items in Launcher activity */ diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java new file mode 100644 index 0000000000..19a4c5d84c --- /dev/null +++ b/src/com/android/launcher3/views/SearchResultPlayItem.java @@ -0,0 +1,154 @@ +/* + * 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.views; + +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.net.Uri; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; +import com.android.launcher3.allapps.search.AllAppsSearchBarController; + +import java.io.IOException; +import java.net.URL; + +/** + * A View representing a PlayStore item. + */ +public class SearchResultPlayItem extends LinearLayout implements + AllAppsSearchBarController.PayloadResultHandler { + private final DeviceProfile mDeviceProfile; + private View mIconView; + private TextView mTitleView; + private TextView[] mDetailViews = new TextView[3]; + private Button mPreviewButton; + private String mPackageName; + private boolean mIsInstantGame; + + public SearchResultPlayItem(Context context) { + this(context, null, 0); + } + + public SearchResultPlayItem(Context context, + @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public SearchResultPlayItem(Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + mDeviceProfile = Launcher.getLauncher(getContext()).getDeviceProfile(); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mIconView = findViewById(R.id.icon); + mTitleView = findViewById(R.id.title_view); + mPreviewButton = findViewById(R.id.try_button); + mPreviewButton.setOnClickListener(view -> launchInstantGame()); + mDetailViews[0] = findViewById(R.id.detail_0); + mDetailViews[1] = findViewById(R.id.detail_1); + mDetailViews[2] = findViewById(R.id.detail_2); + + ViewGroup.LayoutParams iconParams = mIconView.getLayoutParams(); + iconParams.height = mDeviceProfile.allAppsIconSizePx; + iconParams.width = mDeviceProfile.allAppsIconSizePx; + setOnClickListener(view -> handleSelection()); + + } + + @Override + public void applyAdapterInfo(AdapterItemWithPayload adapterItemWithPayload) { + Bundle bundle = adapterItemWithPayload.getPayload(); + adapterItemWithPayload.setSelectionHandler(this::handleSelection); + if (bundle.getString("package", "").equals(mPackageName)) { + return; + } + mIsInstantGame = bundle.getBoolean("instant_game", false); + mPackageName = bundle.getString("package"); + mPreviewButton.setVisibility(mIsInstantGame ? VISIBLE : GONE); + mTitleView.setText(bundle.getString("title")); +// TODO: Should use a generic type to get values b/165320033 + showIfNecessary(mDetailViews[0], bundle.getString("price")); + showIfNecessary(mDetailViews[1], bundle.getString("rating")); + showIfNecessary(mDetailViews[2], bundle.getString("category")); + + mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder); + UI_HELPER_EXECUTOR.execute(() -> { + try { +// TODO: Handle caching + URL url = new URL(bundle.getString("icon_url")); + Bitmap bitmap = BitmapFactory.decodeStream(url.openStream()); + BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), + Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx, + mDeviceProfile.allAppsIconSizePx, false)); + mIconView.post(() -> mIconView.setBackground(bitmapDrawable)); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + private void showIfNecessary(TextView textView, @Nullable String string) { + if (string == null || string.isEmpty()) { + textView.setVisibility(GONE); + } else { + textView.setText(string); + textView.setVisibility(VISIBLE); + } + } + + private void handleSelection() { + if (mPackageName == null) return; + Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse( + "https://play.google.com/store/apps/details?id=" + + mPackageName)); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getContext().startActivity(i); + } + + private void launchInstantGame() { + if (!mIsInstantGame) return; + Intent intent = new Intent(Intent.ACTION_VIEW); + String referrer = "Pixel_Launcher"; + String id = mPackageName; + String deepLinkUrl = "market://details?id=" + id + "&launch=true&referrer=" + referrer; + intent.setPackage("com.android.vending"); + intent.setData(Uri.parse(deepLinkUrl)); + intent.putExtra("overlay", true); + intent.putExtra("callerId", getContext().getPackageName()); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getContext().startActivity(intent); + } +} diff --git a/src/com/android/launcher3/views/SearchSectionHeaderView.java b/src/com/android/launcher3/views/SearchSectionHeaderView.java new file mode 100644 index 0000000000..d439ee3d68 --- /dev/null +++ b/src/com/android/launcher3/views/SearchSectionHeaderView.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 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.util.AttributeSet; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import com.android.launcher3.allapps.AllAppsGridAdapter; +import com.android.launcher3.allapps.search.AllAppsSearchBarController; + +/** + * Header text view that shows a title for a given section in All apps search + */ +public class SearchSectionHeaderView extends TextView implements + AllAppsSearchBarController.PayloadResultHandler { + public SearchSectionHeaderView(Context context) { + super(context); + } + + public SearchSectionHeaderView(Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public SearchSectionHeaderView(Context context, @Nullable AttributeSet attrs, int styleAttr) { + super(context, attrs, styleAttr); + } + + @Override + public void applyAdapterInfo(AllAppsGridAdapter.AdapterItemWithPayload adapterItem) { + String title = adapterItem.getPayload(); + if (title == null || !title.isEmpty()) { + setText(title); + setVisibility(VISIBLE); + } else { + setVisibility(INVISIBLE); + } + } +}