Merging from ub-launcher3-master @ build 6777814
Test: manual, presubmit on the source branch x20/teams/android-launcher/merge/ub-launcher3-master_master_6777814.html Change-Id: I0bb722c8f29e91cb382337f8b11bcf43d711949b
This commit is contained in:
@@ -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<WellbeingModel> 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<Bundle> consumer) {
|
||||
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG || mIsInTest) {
|
||||
Log.d(TAG, "runWithMinimalDeviceConfigs() called");
|
||||
}
|
||||
|
||||
@@ -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<T extends RecentsView>
|
||||
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<T extends RecentsView>
|
||||
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(),
|
||||
|
||||
+6
-6
@@ -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);
|
||||
}
|
||||
|
||||
+10
-2
@@ -82,7 +82,15 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
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<T extends BaseDraggingActivity>
|
||||
clearState();
|
||||
}
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
mNoIntercept = !canInterceptTouch();
|
||||
mNoIntercept = !canInterceptTouch(ev);
|
||||
if (mNoIntercept) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -664,17 +664,17 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
runningComponent != null && runningComponent.equals(homeComponent);
|
||||
}
|
||||
|
||||
if (gestureState.getRunningTask() == null) {
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()
|
||||
&& gestureState.getActivityInterface().isInLiveTileMode()) {
|
||||
return createOverviewInputConsumer(
|
||||
previousGestureState, gestureState, event, forceOverviewInputConsumer);
|
||||
} else if (gestureState.getRunningTask() == null) {
|
||||
return mResetGestureInputConsumer;
|
||||
} else if (previousGestureState.isRunningAnimationToLauncher()
|
||||
|| gestureState.getActivityInterface().isResumed()
|
||||
|| forceOverviewInputConsumer) {
|
||||
return createOverviewInputConsumer(
|
||||
previousGestureState, gestureState, event, forceOverviewInputConsumer);
|
||||
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get()
|
||||
&& gestureState.getActivityInterface().isInLiveTileMode()) {
|
||||
return createOverviewInputConsumer(
|
||||
previousGestureState, gestureState, event, forceOverviewInputConsumer);
|
||||
} else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
|
||||
return mResetGestureInputConsumer;
|
||||
} else {
|
||||
|
||||
@@ -40,14 +40,13 @@ public class LiveTileOverlay extends Drawable {
|
||||
public static final LiveTileOverlay INSTANCE = new LiveTileOverlay();
|
||||
|
||||
private final Paint mPaint = new Paint();
|
||||
private final RectF mCurrentRect = new RectF();
|
||||
private final Rect mBoundsRect = new Rect();
|
||||
|
||||
private RectF mCurrentRect;
|
||||
private float mCornerRadius;
|
||||
private Drawable mIcon;
|
||||
private Animator mIconAnimator;
|
||||
|
||||
private boolean mDrawEnabled = true;
|
||||
private float mIconAnimationProgress = 0f;
|
||||
private boolean mIsAttached;
|
||||
|
||||
@@ -58,7 +57,7 @@ public class LiveTileOverlay extends Drawable {
|
||||
public void update(RectF currentRect, float cornerRadius) {
|
||||
invalidateSelf();
|
||||
|
||||
mCurrentRect = currentRect;
|
||||
mCurrentRect.set(currentRect);
|
||||
mCornerRadius = cornerRadius;
|
||||
|
||||
mCurrentRect.roundOut(mBoundsRect);
|
||||
@@ -93,16 +92,9 @@ public class LiveTileOverlay extends Drawable {
|
||||
return mIconAnimationProgress;
|
||||
}
|
||||
|
||||
public void setDrawEnabled(boolean drawEnabled) {
|
||||
if (mDrawEnabled != drawEnabled) {
|
||||
mDrawEnabled = drawEnabled;
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mCurrentRect != null && mDrawEnabled) {
|
||||
if (mCurrentRect != null) {
|
||||
canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
|
||||
if (mIcon != null && mIconAnimationProgress > 0f) {
|
||||
canvas.save();
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<com.android.launcher3.views.SearchResultPlayItem xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:orientation="horizontal">
|
||||
<View
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/deep_shortcut_icon_size"
|
||||
android:layout_height="@dimen/deep_shortcut_icon_size"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:background="@drawable/ic_deepshortcut_placeholder" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_view"
|
||||
style="@style/TextHeadline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detail_0"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detail_1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detail_2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
<Button
|
||||
android:id="@+id/try_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:text="@string/search_action_try_now">
|
||||
</Button>
|
||||
|
||||
|
||||
</com.android.launcher3.views.SearchResultPlayItem>
|
||||
@@ -13,11 +13,11 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/section_title"
|
||||
android:textSize="14sp"
|
||||
android:fontFamily="@style/TextHeadline"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:padding="4dp"
|
||||
android:layout_height="wrap_content"/>
|
||||
<com.android.launcher3.views.SearchSectionHeaderView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/section_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@style/TextHeadline"
|
||||
android:padding="4dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="14sp" />
|
||||
@@ -70,6 +70,8 @@
|
||||
<!--All apps Search-->
|
||||
<!-- Section title for apps [CHAR_LIMIT=50] -->
|
||||
<string name="search_corpus_apps">Apps</string>
|
||||
<!-- try instant app action for play search result [CHAR_LIMIT=50 -->
|
||||
<string name="search_action_try_now">Try Now</string>
|
||||
|
||||
<!-- Popup items -->
|
||||
<!-- Text to display as the header above notifications. [CHAR_LIMIT=30] -->
|
||||
|
||||
@@ -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<LauncherState> 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<LauncherState> 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<LauncherState> 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<LauncherState> 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<LauncherState> 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<LauncherState> 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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<WorkspacePageIndicator>
|
||||
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();
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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<AllAppsGridAdapter.
|
||||
|
||||
public static final int VIEW_TYPE_SEARCH_HERO_APP = 1 << 6;
|
||||
|
||||
public static final int DETAIL_ROW_WITH_BUTTON = 1 << 7;
|
||||
|
||||
// Common view type masks
|
||||
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
|
||||
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
|
||||
@@ -85,6 +87,108 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Factory method for AppIcon AdapterItem
|
||||
*/
|
||||
public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
|
||||
int appIndex) {
|
||||
AdapterItem item = new AdapterItem();
|
||||
item.viewType = VIEW_TYPE_ICON;
|
||||
item.position = pos;
|
||||
item.sectionName = sectionName;
|
||||
item.appInfo = appInfo;
|
||||
item.appIndex = appIndex;
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for empty search results view
|
||||
*/
|
||||
public static AdapterItem asEmptySearch(int pos) {
|
||||
AdapterItem item = new AdapterItem();
|
||||
item.viewType = VIEW_TYPE_EMPTY_SEARCH;
|
||||
item.position = pos;
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for a dividerView in AllAppsSearch
|
||||
*/
|
||||
public static AdapterItem asAllAppsDivider(int pos) {
|
||||
AdapterItem item = new AdapterItem();
|
||||
item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER;
|
||||
item.position = pos;
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for a market search button
|
||||
*/
|
||||
public static AdapterItem asMarketSearch(int pos) {
|
||||
AdapterItem item = new AdapterItem();
|
||||
item.viewType = VIEW_TYPE_SEARCH_MARKET;
|
||||
item.position = pos;
|
||||
return item;
|
||||
}
|
||||
|
||||
boolean isCountedForAccessibility() {
|
||||
return viewType == VIEW_TYPE_ICON
|
||||
|| viewType == VIEW_TYPE_SEARCH_HERO_APP
|
||||
|| viewType == DETAIL_ROW_WITH_BUTTON;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension of AdapterItem that contains an extra payload specific to item
|
||||
* @param <T> Play load Type
|
||||
*/
|
||||
public static class AdapterItemWithPayload<T> 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<AllAppsGridAdapter.
|
||||
case VIEW_TYPE_SEARCH_HERO_APP:
|
||||
return new ViewHolder(mLayoutInflater.inflate(
|
||||
R.layout.search_result_hero_app, parent, false));
|
||||
case DETAIL_ROW_WITH_BUTTON:
|
||||
return new ViewHolder(mLayoutInflater.inflate(
|
||||
R.layout.search_result_play_item, parent, false));
|
||||
default:
|
||||
throw new RuntimeException("Unexpected view type");
|
||||
}
|
||||
@@ -315,15 +422,11 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
}
|
||||
break;
|
||||
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
|
||||
TextView titleView = (TextView) holder.itemView;
|
||||
titleView.setText(mApps.getAdapterItems().get(position).searchSectionInfo.getTitle(
|
||||
titleView.getContext()));
|
||||
break;
|
||||
case DETAIL_ROW_WITH_BUTTON:
|
||||
case VIEW_TYPE_SEARCH_HERO_APP:
|
||||
HeroSearchResultView heroView = (HeroSearchResultView) holder.itemView;
|
||||
heroView.prepareUsingAdapterItem(
|
||||
(AlphabeticalAppsList.HeroAppAdapterItem) mApps.getAdapterItems().get(
|
||||
position));
|
||||
PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
|
||||
payloadResultView.applyAdapterInfo(
|
||||
(AdapterItemWithPayload) mApps.getAdapterItems().get(position));
|
||||
break;
|
||||
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
||||
// nothing to do
|
||||
@@ -344,7 +447,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
|
||||
AdapterItem item = mApps.getAdapterItems().get(position);
|
||||
return item.viewType;
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
|
||||
if (mApps == null) {
|
||||
return;
|
||||
}
|
||||
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
|
||||
List<AllAppsGridAdapter.AdapterItem> 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<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
|
||||
List<AllAppsGridAdapter.AdapterItem> 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<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
|
||||
AlphabeticalAppsList.AdapterItem posItem = position < items.size() ?
|
||||
items.get(position) : null;
|
||||
List<AllAppsGridAdapter.AdapterItem> 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 &&
|
||||
|
||||
@@ -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<AlphabeticalAppsList.AdapterItem> adapterItems = mAppsView.getApps().getAdapterItems();
|
||||
List<AllAppsGridAdapter.AdapterItem> 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);
|
||||
|
||||
@@ -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<WorkspaceItemInfo> mShortcutInfos;
|
||||
|
||||
public HeroAppAdapterItem(AppInfo info, ArrayList<WorkspaceItemInfo> shortcutInfos) {
|
||||
viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_HERO_APP;
|
||||
mShortcutInfos = shortcutInfos;
|
||||
appInfo = info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of shortcuts for appInfo
|
||||
*/
|
||||
public ArrayList<WorkspaceItemInfo> 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);
|
||||
}
|
||||
|
||||
@@ -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<AllAppsSearchPlugin> {
|
||||
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<AllAppsSearchPlugin> plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,11 +195,23 @@ public class AllAppsSearchBarController
|
||||
*
|
||||
* @param items sorted list of search result adapter items.
|
||||
*/
|
||||
void onSearchResult(String query, ArrayList<AlphabeticalAppsList.AdapterItem> items);
|
||||
void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items);
|
||||
|
||||
/**
|
||||
* Called when the search results should be cleared.
|
||||
*/
|
||||
void clearSearchResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for supporting dynamic search results
|
||||
*
|
||||
* @param <T> Type of payload
|
||||
*/
|
||||
public interface PayloadResultHandler<T> {
|
||||
/**
|
||||
* Updates View using Adapter's payload
|
||||
*/
|
||||
void applyAdapterInfo(AdapterItemWithPayload<T> adapterItemWithPayload);
|
||||
}
|
||||
}
|
||||
@@ -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<AlphabeticalAppsList.AdapterItem> items) {
|
||||
public void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items) {
|
||||
if (items != null) {
|
||||
mApps.setSearchResults(items);
|
||||
notifyResultChanged();
|
||||
|
||||
@@ -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<WorkspaceItemInfo> getShortcutInfos(Context context, AppInfo appInfo) {
|
||||
List<ShortcutInfo> shortcuts = new ShortcutRequest(context, appInfo.user)
|
||||
@@ -126,7 +127,9 @@ public class AppsSearchPipeline implements SearchPipeline {
|
||||
//hero app
|
||||
AppInfo appInfo = apps.get(i);
|
||||
ArrayList<WorkspaceItemInfo> 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);
|
||||
}
|
||||
|
||||
@@ -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<ArrayList<AlphabeticalAppsList.AdapterItem>> cb);
|
||||
void performSearch(String query, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> cb);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<OverviewScrim> SCRIM_MULTIPLIER =
|
||||
new FloatProperty<OverviewScrim>("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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())));
|
||||
|
||||
+83
-43
@@ -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<ItemInstallQueue> 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<String> 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<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
|
||||
SharedPreferences prefs = Utilities.getPrefs(context);
|
||||
SharedPreferences prefs = Utilities.getPrefs(mContext);
|
||||
Set<String> 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<String> packageNames,
|
||||
UserHandle user) {
|
||||
/**
|
||||
* Removes previously added items from the queue.
|
||||
*/
|
||||
@WorkerThread
|
||||
public void removeFromInstallQueue(HashSet<String> packageNames, UserHandle user) {
|
||||
if (packageNames.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Preconditions.assertWorkerThread();
|
||||
|
||||
SharedPreferences sp = Utilities.getPrefs(context);
|
||||
SharedPreferences sp = Utilities.getPrefs(mContext);
|
||||
Set<String> 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<ShortcutKey> getPendingShortcuts(Context context) {
|
||||
/**
|
||||
* Returns all pending shorts in the queue
|
||||
*/
|
||||
@WorkerThread
|
||||
public HashSet<ShortcutKey> getPendingShortcuts() {
|
||||
HashSet<ShortcutKey> result = new HashSet<>();
|
||||
|
||||
Set<String> strings = Utilities.getPrefs(context).getStringSet(APPS_PENDING_INSTALL, null);
|
||||
Set<String> 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;
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<List<WorkspaceItemInfo>> {
|
||||
|
||||
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<List<WorkspaceItemInfo>> adapterItem) {
|
||||
mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo);
|
||||
mIconView.setBackground(mBubbleTextView.getIcon());
|
||||
mIconView.setTag(adapterItem.appInfo);
|
||||
List<WorkspaceItemInfo> shorcutInfos = adapterItem.getShortcutInfos();
|
||||
List<WorkspaceItemInfo> 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
|
||||
*/
|
||||
|
||||
@@ -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<Bundle> {
|
||||
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<Bundle> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<String> {
|
||||
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<String> adapterItem) {
|
||||
String title = adapterItem.getPayload();
|
||||
if (title == null || !title.isEmpty()) {
|
||||
setText(title);
|
||||
setVisibility(VISIBLE);
|
||||
} else {
|
||||
setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user