Merge "Removing support for lagacy shortcuts" into udc-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e607fdb2a2
@@ -337,7 +337,6 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
|
|||||||
if (getTag() instanceof WorkspaceItemInfo) {
|
if (getTag() instanceof WorkspaceItemInfo) {
|
||||||
WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
|
WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
|
||||||
isBadged = !Process.myUserHandle().equals(info.user)
|
isBadged = !Process.myUserHandle().equals(info.user)
|
||||||
|| info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
|
|
||||||
|| info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
|| info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import static com.android.launcher3.LauncherSettings.Animation.VIEW_BACKGROUND;
|
|||||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
|
||||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||||
import static com.android.launcher3.LauncherState.NORMAL;
|
import static com.android.launcher3.LauncherState.NORMAL;
|
||||||
import static com.android.launcher3.LauncherState.NO_OFFSET;
|
import static com.android.launcher3.LauncherState.NO_OFFSET;
|
||||||
@@ -283,7 +282,6 @@ public class QuickstepLauncher extends Launcher {
|
|||||||
|
|
||||||
if (mAllAppsPredictions != null
|
if (mAllAppsPredictions != null
|
||||||
&& (info.itemType == ITEM_TYPE_APPLICATION
|
&& (info.itemType == ITEM_TYPE_APPLICATION
|
||||||
|| info.itemType == ITEM_TYPE_SHORTCUT
|
|
||||||
|| info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) {
|
|| info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) {
|
||||||
int count = mAllAppsPredictions.items.size();
|
int count = mAllAppsPredictions.items.size();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
@@ -1162,7 +1160,6 @@ public class QuickstepLauncher extends Launcher {
|
|||||||
}
|
}
|
||||||
switch (info.itemType) {
|
switch (info.itemType) {
|
||||||
case Favorites.ITEM_TYPE_APPLICATION:
|
case Favorites.ITEM_TYPE_APPLICATION:
|
||||||
case Favorites.ITEM_TYPE_SHORTCUT:
|
|
||||||
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||||
case Favorites.ITEM_TYPE_APPWIDGET:
|
case Favorites.ITEM_TYPE_APPWIDGET:
|
||||||
// Fall through and continue if it's an app, shortcut, or widget
|
// Fall through and continue if it's an app, shortcut, or widget
|
||||||
|
|||||||
+67
-75
@@ -20,6 +20,7 @@ import static android.os.Process.myUserHandle;
|
|||||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
|
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
|
||||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||||
|
import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
|
||||||
import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
|
import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
@@ -40,11 +41,9 @@ import android.text.TextUtils;
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.launcher3.LauncherAppState;
|
|
||||||
import com.android.launcher3.icons.ComponentWithLabel;
|
|
||||||
import com.android.launcher3.icons.IconCache;
|
|
||||||
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
|
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
|
||||||
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
|
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
|
||||||
|
import com.android.launcher3.util.LauncherLayoutBuilder;
|
||||||
import com.android.launcher3.util.LauncherModelHelper;
|
import com.android.launcher3.util.LauncherModelHelper;
|
||||||
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
||||||
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
||||||
@@ -53,8 +52,6 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -76,17 +73,9 @@ public final class WidgetsPredicationUpdateTaskTest {
|
|||||||
private LauncherModelHelper mModelHelper;
|
private LauncherModelHelper mModelHelper;
|
||||||
private UserHandle mUserHandle;
|
private UserHandle mUserHandle;
|
||||||
|
|
||||||
@Mock
|
|
||||||
private IconCache mIconCache;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
mModelHelper = new LauncherModelHelper();
|
mModelHelper = new LauncherModelHelper();
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
doAnswer(invocation -> {
|
|
||||||
ComponentWithLabel componentWithLabel = invocation.getArgument(0);
|
|
||||||
return componentWithLabel.getComponent().getShortClassName();
|
|
||||||
}).when(mIconCache).getTitleNoCache(any());
|
|
||||||
|
|
||||||
mUserHandle = myUserHandle();
|
mUserHandle = myUserHandle();
|
||||||
mApp1Provider1 = createAppWidgetProviderInfo(
|
mApp1Provider1 = createAppWidgetProviderInfo(
|
||||||
@@ -114,16 +103,12 @@ public final class WidgetsPredicationUpdateTaskTest {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}).when(manager).getInstalledProvidersForPackage(any(), eq(myUserHandle()));
|
}).when(manager).getInstalledProvidersForPackage(any(), eq(myUserHandle()));
|
||||||
|
|
||||||
// 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace.
|
LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
|
||||||
mModelHelper.initializeData("widgets_predication_update_task_data");
|
.atWorkspace(0, 1, 2).putWidget("app4", "provider1", 1, 1)
|
||||||
|
.atWorkspace(0, 1, 3).putWidget("app5", "provider1", 1, 1);
|
||||||
|
mModelHelper.setupDefaultLayoutProvider(builder);
|
||||||
MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(mCallback)).get();
|
MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(mCallback)).get();
|
||||||
MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update(
|
mModelHelper.loadModelSync();
|
||||||
LauncherAppState.getInstance(mModelHelper.sandboxContext),
|
|
||||||
/* packageUser= */ null));
|
|
||||||
|
|
||||||
MODEL_EXECUTOR.submit(() -> { }).get();
|
|
||||||
MAIN_EXECUTOR.submit(() -> { }).get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -132,65 +117,72 @@ public final class WidgetsPredicationUpdateTaskTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
|
public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() {
|
||||||
throws Exception {
|
// Run on model executor so that no other task runs in the middle.
|
||||||
// WHEN newPredicationTask is executed with app predication of 5 apps.
|
runOnExecutorSync(MODEL_EXECUTOR, () -> {
|
||||||
AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
|
// WHEN newPredicationTask is executed with app predication of 5 apps.
|
||||||
mUserHandle);
|
AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
|
||||||
AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1",
|
mUserHandle);
|
||||||
mUserHandle);
|
AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1",
|
||||||
AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
|
mUserHandle);
|
||||||
mUserHandle);
|
AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
|
||||||
AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
|
mUserHandle);
|
||||||
mUserHandle);
|
AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
|
||||||
AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
|
mUserHandle);
|
||||||
mUserHandle);
|
AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
|
||||||
mModelHelper.executeTaskForTest(
|
mUserHandle);
|
||||||
newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
|
mCallback.mRecommendedWidgets = null;
|
||||||
.forEach(Runnable::run);
|
mModelHelper.getModel().enqueueModelUpdateTask(
|
||||||
|
newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)));
|
||||||
|
runOnExecutorSync(MAIN_EXECUTOR, () -> { });
|
||||||
|
|
||||||
// THEN only 2 widgets are returned because
|
// THEN only 2 widgets are returned because
|
||||||
// 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
|
// 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
|
||||||
// excluded from the result.
|
// excluded from the result.
|
||||||
// 2. app3 doesn't have a widget.
|
// 2. app3 doesn't have a widget.
|
||||||
// 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
|
// 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
|
||||||
List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
|
List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
|
||||||
.stream()
|
.stream()
|
||||||
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
|
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
assertThat(recommendedWidgets).hasSize(2);
|
assertThat(recommendedWidgets).hasSize(2);
|
||||||
assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
|
assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
|
||||||
assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
|
assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty()
|
public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty() {
|
||||||
throws Exception {
|
runOnExecutorSync(MODEL_EXECUTOR, () -> {
|
||||||
|
|
||||||
// Not installed widget
|
// Not installed widget
|
||||||
AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3",
|
AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3",
|
||||||
mUserHandle);
|
mUserHandle);
|
||||||
// Not installed app
|
// Not installed app
|
||||||
AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
|
AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
|
||||||
mUserHandle);
|
mUserHandle);
|
||||||
// Workspace added widgets
|
// Workspace added widgets
|
||||||
AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
|
AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
|
||||||
mUserHandle);
|
mUserHandle);
|
||||||
AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
|
AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
|
||||||
mUserHandle);
|
mUserHandle);
|
||||||
mModelHelper.executeTaskForTest(
|
|
||||||
newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1)))
|
|
||||||
.forEach(Runnable::run);
|
|
||||||
|
|
||||||
// THEN only 2 widgets are returned because the launcher only filters out non-exist widgets.
|
mCallback.mRecommendedWidgets = null;
|
||||||
List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
|
mModelHelper.getModel().enqueueModelUpdateTask(
|
||||||
.stream()
|
newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1)));
|
||||||
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
|
runOnExecutorSync(MAIN_EXECUTOR, () -> { });
|
||||||
.collect(Collectors.toList());
|
|
||||||
assertThat(recommendedWidgets).hasSize(2);
|
// THEN only 2 widgets are returned because the launcher only filters out
|
||||||
// Another widget from the same package
|
// non-exist widgets.
|
||||||
assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
|
List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
|
||||||
assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
|
.stream()
|
||||||
|
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertThat(recommendedWidgets).hasSize(2);
|
||||||
|
// Another widget from the same package
|
||||||
|
assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
|
||||||
|
assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertWidgetInfo(
|
private void assertWidgetInfo(
|
||||||
|
|||||||
@@ -1928,7 +1928,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
|||||||
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
|
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
|
||||||
addAppWidgetFromDrop((PendingAddWidgetInfo) info);
|
addAppWidgetFromDrop((PendingAddWidgetInfo) info);
|
||||||
break;
|
break;
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||||
processShortcutFromDrop((PendingAddShortcutInfo) info);
|
processShortcutFromDrop((PendingAddShortcutInfo) info);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -2435,7 +2435,6 @@ public class Launcher extends StatefulActivity<LauncherState>
|
|||||||
final View view;
|
final View view;
|
||||||
switch (item.itemType) {
|
switch (item.itemType) {
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
|
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
|
||||||
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
|
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
|
||||||
view = createShortcut(info);
|
view = createShortcut(info);
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ public class LauncherAppState implements SafeCloseable {
|
|||||||
});
|
});
|
||||||
|
|
||||||
mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
|
mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
|
||||||
|
mOnTerminateCallback.add(() ->
|
||||||
|
mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel));
|
||||||
|
|
||||||
SimpleBroadcastReceiver modelChangeReceiver =
|
SimpleBroadcastReceiver modelChangeReceiver =
|
||||||
new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
|
new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
|
||||||
@@ -123,8 +125,9 @@ public class LauncherAppState implements SafeCloseable {
|
|||||||
mOnTerminateCallback.add(userChangeListener::close);
|
mOnTerminateCallback.add(userChangeListener::close);
|
||||||
|
|
||||||
LockedUserState.get(context).runOnUserUnlocked(() -> {
|
LockedUserState.get(context).runOnUserUnlocked(() -> {
|
||||||
CustomWidgetManager.INSTANCE.get(mContext)
|
CustomWidgetManager cwm = CustomWidgetManager.INSTANCE.get(mContext);
|
||||||
.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
|
cwm.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
|
||||||
|
mOnTerminateCallback.add(() -> cwm.setWidgetRefreshCallback(null));
|
||||||
|
|
||||||
IconObserver observer = new IconObserver();
|
IconObserver observer = new IconObserver();
|
||||||
SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
|
SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
|
||||||
@@ -159,6 +162,7 @@ public class LauncherAppState implements SafeCloseable {
|
|||||||
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
|
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
|
||||||
iconCacheFileName != null);
|
iconCacheFileName != null);
|
||||||
mOnTerminateCallback.add(mIconCache::close);
|
mOnTerminateCallback.add(mIconCache::close);
|
||||||
|
mOnTerminateCallback.add(mModel::destroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
|
private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
|
||||||
@@ -180,9 +184,6 @@ public class LauncherAppState implements SafeCloseable {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
mModel.destroy();
|
|
||||||
mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
|
|
||||||
CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
|
|
||||||
mOnTerminateCallback.executeAllAndDestroy();
|
mOnTerminateCallback.executeAllAndDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ public class LauncherSettings {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The gesture is an application created shortcut
|
* The gesture is an application created shortcut
|
||||||
|
* @deprecated This is no longer supported. Use {@link #ITEM_TYPE_DEEP_SHORTCUT} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static final int ITEM_TYPE_SHORTCUT = 1;
|
public static final int ITEM_TYPE_SHORTCUT = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,7 +215,6 @@ public class LauncherSettings {
|
|||||||
public static final String itemTypeToString(int type) {
|
public static final String itemTypeToString(int type) {
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case ITEM_TYPE_APPLICATION: return "APP";
|
case ITEM_TYPE_APPLICATION: return "APP";
|
||||||
case ITEM_TYPE_SHORTCUT: return "SHORTCUT";
|
|
||||||
case ITEM_TYPE_FOLDER: return "FOLDER";
|
case ITEM_TYPE_FOLDER: return "FOLDER";
|
||||||
case ITEM_TYPE_APPWIDGET: return "WIDGET";
|
case ITEM_TYPE_APPWIDGET: return "WIDGET";
|
||||||
case ITEM_TYPE_CUSTOM_APPWIDGET: return "CUSTOMWIDGET";
|
case ITEM_TYPE_CUSTOM_APPWIDGET: return "CUSTOMWIDGET";
|
||||||
|
|||||||
@@ -1840,7 +1840,6 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
|||||||
!= LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
|
!= LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
|
||||||
boolean willBecomeShortcut =
|
boolean willBecomeShortcut =
|
||||||
(info.itemType == ITEM_TYPE_APPLICATION ||
|
(info.itemType == ITEM_TYPE_APPLICATION ||
|
||||||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
|
|
||||||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
|
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
|
||||||
|
|
||||||
return (aboveShortcut && willBecomeShortcut);
|
return (aboveShortcut && willBecomeShortcut);
|
||||||
@@ -2759,7 +2758,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
|||||||
final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) info;
|
final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) info;
|
||||||
|
|
||||||
boolean findNearestVacantCell = true;
|
boolean findNearestVacantCell = true;
|
||||||
if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
|
if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
|
||||||
mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
|
mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
|
||||||
cellLayout, mTargetCell);
|
cellLayout, mTargetCell);
|
||||||
float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter(
|
float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter(
|
||||||
@@ -2832,8 +2831,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
|||||||
View view;
|
View view;
|
||||||
|
|
||||||
switch (info.itemType) {
|
switch (info.itemType) {
|
||||||
case ITEM_TYPE_APPLICATION:
|
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION:
|
case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION:
|
||||||
if (info instanceof WorkspaceItemFactory) {
|
if (info instanceof WorkspaceItemFactory) {
|
||||||
|
|||||||
@@ -881,7 +881,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||||||
final ItemInfo item = d.dragInfo;
|
final ItemInfo item = d.dragInfo;
|
||||||
final int itemType = item.itemType;
|
final int itemType = item.itemType;
|
||||||
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
|
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
|
||||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
|
|
||||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT));
|
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -260,7 +260,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
|||||||
private boolean willAcceptItem(ItemInfo item) {
|
private boolean willAcceptItem(ItemInfo item) {
|
||||||
final int itemType = item.itemType;
|
final int itemType = item.itemType;
|
||||||
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
|
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
|
||||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
|
|
||||||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
|
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
|
||||||
item != mInfo && !mFolder.isOpen());
|
item != mInfo && !mFolder.isOpen());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,13 +34,9 @@ import android.appwidget.AppWidgetHostView;
|
|||||||
import android.appwidget.AppWidgetProviderInfo;
|
import android.appwidget.AppWidgetProviderInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.ContextWrapper;
|
import android.content.ContextWrapper;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
|
||||||
import android.graphics.drawable.ColorDrawable;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@@ -78,8 +74,6 @@ import com.android.launcher3.celllayout.CellLayoutLayoutParams;
|
|||||||
import com.android.launcher3.celllayout.CellPosMapper;
|
import com.android.launcher3.celllayout.CellPosMapper;
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
import com.android.launcher3.folder.FolderIcon;
|
import com.android.launcher3.folder.FolderIcon;
|
||||||
import com.android.launcher3.icons.BaseIconFactory;
|
|
||||||
import com.android.launcher3.icons.BitmapInfo;
|
|
||||||
import com.android.launcher3.icons.LauncherIcons;
|
import com.android.launcher3.icons.LauncherIcons;
|
||||||
import com.android.launcher3.model.BgDataModel;
|
import com.android.launcher3.model.BgDataModel;
|
||||||
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
|
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
|
||||||
@@ -183,7 +177,6 @@ public class LauncherPreviewRenderer extends ContextWrapper
|
|||||||
private final DeviceProfile mDp;
|
private final DeviceProfile mDp;
|
||||||
private final DeviceProfile mDpOrig;
|
private final DeviceProfile mDpOrig;
|
||||||
private final Rect mInsets;
|
private final Rect mInsets;
|
||||||
private final WorkspaceItemInfo mWorkspaceItemInfo;
|
|
||||||
private final LayoutInflater mHomeElementInflater;
|
private final LayoutInflater mHomeElementInflater;
|
||||||
private final InsettableFrameLayout mRootView;
|
private final InsettableFrameLayout mRootView;
|
||||||
private final Hotseat mHotseat;
|
private final Hotseat mHotseat;
|
||||||
@@ -221,19 +214,6 @@ public class LauncherPreviewRenderer extends ContextWrapper
|
|||||||
mDp.isTaskbarPresent ? 0 : currentWindowInsets.getSystemWindowInsetBottom());
|
mDp.isTaskbarPresent ? 0 : currentWindowInsets.getSystemWindowInsetBottom());
|
||||||
mDp.updateInsets(mInsets);
|
mDp.updateInsets(mInsets);
|
||||||
|
|
||||||
BaseIconFactory iconFactory =
|
|
||||||
new BaseIconFactory(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize) { };
|
|
||||||
BitmapInfo iconInfo = iconFactory.createBadgedIconBitmap(
|
|
||||||
new AdaptiveIconDrawable(
|
|
||||||
new ColorDrawable(Color.WHITE),
|
|
||||||
new ColorDrawable(Color.WHITE)));
|
|
||||||
|
|
||||||
mWorkspaceItemInfo = new WorkspaceItemInfo();
|
|
||||||
mWorkspaceItemInfo.bitmap = iconInfo;
|
|
||||||
mWorkspaceItemInfo.intent = new Intent();
|
|
||||||
mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
|
|
||||||
context.getString(R.string.label_application);
|
|
||||||
|
|
||||||
mHomeElementInflater = LayoutInflater.from(
|
mHomeElementInflater = LayoutInflater.from(
|
||||||
new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
|
new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
|
||||||
mHomeElementInflater.setFactory2(this);
|
mHomeElementInflater.setFactory2(this);
|
||||||
@@ -483,7 +463,6 @@ public class LauncherPreviewRenderer extends ContextWrapper
|
|||||||
for (ItemInfo itemInfo : currentWorkspaceItems) {
|
for (ItemInfo itemInfo : currentWorkspaceItems) {
|
||||||
switch (itemInfo.itemType) {
|
switch (itemInfo.itemType) {
|
||||||
case Favorites.ITEM_TYPE_APPLICATION:
|
case Favorites.ITEM_TYPE_APPLICATION:
|
||||||
case Favorites.ITEM_TYPE_SHORTCUT:
|
|
||||||
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||||
inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
|
inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -91,8 +91,7 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
|
|||||||
List<ItemInfo> filteredItems = new ArrayList<>();
|
List<ItemInfo> filteredItems = new ArrayList<>();
|
||||||
for (Pair<ItemInfo, Object> entry : mItemList) {
|
for (Pair<ItemInfo, Object> entry : mItemList) {
|
||||||
ItemInfo item = entry.first;
|
ItemInfo item = entry.first;
|
||||||
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
|
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
|
||||||
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
|
|
||||||
// Short-circuit this logic if the icon exists somewhere on the workspace
|
// Short-circuit this logic if the icon exists somewhere on the workspace
|
||||||
if (shortcutExists(dataModel, item.getIntent(), item.user)) {
|
if (shortcutExists(dataModel, item.getIntent(), item.user)) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -210,7 +210,6 @@ public class BgDataModel {
|
|||||||
// Fall through.
|
// Fall through.
|
||||||
}
|
}
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
|
||||||
workspaceItems.remove(item);
|
workspaceItems.remove(item);
|
||||||
break;
|
break;
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
|
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
|
||||||
@@ -245,7 +244,6 @@ public class BgDataModel {
|
|||||||
break;
|
break;
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
|
||||||
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
|
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
|
||||||
item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
|
item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
|
||||||
workspaceItems.add(item);
|
workspaceItems.add(item);
|
||||||
|
|||||||
@@ -455,7 +455,6 @@ public class GridSizeMigrationUtil {
|
|||||||
try {
|
try {
|
||||||
// calculate weight
|
// calculate weight
|
||||||
switch (entry.itemType) {
|
switch (entry.itemType) {
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
|
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
|
||||||
entry.mIntent = c.getString(indexIntent);
|
entry.mIntent = c.getString(indexIntent);
|
||||||
@@ -531,7 +530,6 @@ public class GridSizeMigrationUtil {
|
|||||||
try {
|
try {
|
||||||
// calculate weight
|
// calculate weight
|
||||||
switch (entry.itemType) {
|
switch (entry.itemType) {
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
|
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||||
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
|
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
|
||||||
entry.mIntent = c.getString(indexIntent);
|
entry.mIntent = c.getString(indexIntent);
|
||||||
|
|||||||
@@ -286,7 +286,6 @@ public class ItemInstallQueue {
|
|||||||
|
|
||||||
final WorkspaceItemInfo si = new WorkspaceItemInfo();
|
final WorkspaceItemInfo si = new WorkspaceItemInfo();
|
||||||
si.user = user;
|
si.user = user;
|
||||||
si.itemType = ITEM_TYPE_APPLICATION;
|
|
||||||
|
|
||||||
LauncherActivityInfo lai;
|
LauncherActivityInfo lai;
|
||||||
boolean usePackageIcon = laiList.isEmpty();
|
boolean usePackageIcon = laiList.isEmpty();
|
||||||
|
|||||||
@@ -193,9 +193,7 @@ public class LoaderCursor extends CursorWrapper {
|
|||||||
|
|
||||||
public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
|
public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
|
||||||
WorkspaceItemInfo wai, boolean useLowResIcon) {
|
WorkspaceItemInfo wai, boolean useLowResIcon) {
|
||||||
byte[] iconBlob = itemType == Favorites.ITEM_TYPE_SHORTCUT
|
byte[] iconBlob = itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT || restoreFlag != 0
|
||||||
|| itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
|
|
||||||
|| restoreFlag != 0
|
|
||||||
? getIconBlob() : null;
|
? getIconBlob() : null;
|
||||||
|
|
||||||
return new IconRequestInfo<>(wai, mActivityInfo, iconBlob, useLowResIcon);
|
return new IconRequestInfo<>(wai, mActivityInfo, iconBlob, useLowResIcon);
|
||||||
@@ -347,7 +345,6 @@ public class LoaderCursor extends CursorWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final WorkspaceItemInfo info = new WorkspaceItemInfo();
|
final WorkspaceItemInfo info = new WorkspaceItemInfo();
|
||||||
info.itemType = Favorites.ITEM_TYPE_APPLICATION;
|
|
||||||
info.user = user;
|
info.user = user;
|
||||||
info.intent = newIntent;
|
info.intent = newIntent;
|
||||||
|
|
||||||
|
|||||||
@@ -503,7 +503,6 @@ public class LoaderTask implements Runnable {
|
|||||||
|
|
||||||
boolean allowMissingTarget = false;
|
boolean allowMissingTarget = false;
|
||||||
switch (c.itemType) {
|
switch (c.itemType) {
|
||||||
case Favorites.ITEM_TYPE_SHORTCUT:
|
|
||||||
case Favorites.ITEM_TYPE_APPLICATION:
|
case Favorites.ITEM_TYPE_APPLICATION:
|
||||||
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||||
Intent intent = c.parseIntent();
|
Intent intent = c.parseIntent();
|
||||||
@@ -517,9 +516,8 @@ public class LoaderTask implements Runnable {
|
|||||||
ComponentName cn = intent.getComponent();
|
ComponentName cn = intent.getComponent();
|
||||||
String targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
|
String targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
|
||||||
|
|
||||||
if (TextUtils.isEmpty(targetPkg)
|
if (TextUtils.isEmpty(targetPkg)) {
|
||||||
&& c.itemType != Favorites.ITEM_TYPE_SHORTCUT) {
|
c.markDeleted("Shortcuts can't have null package");
|
||||||
c.markDeleted("Only legacy shortcuts can have null package");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -498,7 +498,6 @@ public class ModelWriter {
|
|||||||
modelItem.container == Favorites.CONTAINER_HOTSEAT)) {
|
modelItem.container == Favorites.CONTAINER_HOTSEAT)) {
|
||||||
switch (modelItem.itemType) {
|
switch (modelItem.itemType) {
|
||||||
case Favorites.ITEM_TYPE_APPLICATION:
|
case Favorites.ITEM_TYPE_APPLICATION:
|
||||||
case Favorites.ITEM_TYPE_SHORTCUT:
|
|
||||||
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
|
||||||
case Favorites.ITEM_TYPE_FOLDER:
|
case Favorites.ITEM_TYPE_FOLDER:
|
||||||
if (!mBgDataModel.workspaceItems.contains(modelItem)) {
|
if (!mBgDataModel.workspaceItems.contains(modelItem)) {
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINE
|
|||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
|
||||||
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
|
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
|
||||||
import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID;
|
import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID;
|
||||||
@@ -87,7 +86,6 @@ public class ItemInfo {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* One of {@link Favorites#ITEM_TYPE_APPLICATION},
|
* One of {@link Favorites#ITEM_TYPE_APPLICATION},
|
||||||
* {@link Favorites#ITEM_TYPE_SHORTCUT},
|
|
||||||
* {@link Favorites#ITEM_TYPE_DEEP_SHORTCUT}
|
* {@link Favorites#ITEM_TYPE_DEEP_SHORTCUT}
|
||||||
* {@link Favorites#ITEM_TYPE_FOLDER},
|
* {@link Favorites#ITEM_TYPE_FOLDER},
|
||||||
* {@link Favorites#ITEM_TYPE_APP_PAIR},
|
* {@link Favorites#ITEM_TYPE_APP_PAIR},
|
||||||
@@ -361,13 +359,6 @@ public class ItemInfo {
|
|||||||
})
|
})
|
||||||
.orElse(LauncherAtom.Shortcut.newBuilder()));
|
.orElse(LauncherAtom.Shortcut.newBuilder()));
|
||||||
break;
|
break;
|
||||||
case ITEM_TYPE_SHORTCUT:
|
|
||||||
itemBuilder
|
|
||||||
.setShortcut(nullableComponent
|
|
||||||
.map(component -> LauncherAtom.Shortcut.newBuilder()
|
|
||||||
.setShortcutName(component.flattenToShortString()))
|
|
||||||
.orElse(LauncherAtom.Shortcut.newBuilder()));
|
|
||||||
break;
|
|
||||||
case ITEM_TYPE_APPWIDGET:
|
case ITEM_TYPE_APPWIDGET:
|
||||||
itemBuilder
|
itemBuilder
|
||||||
.setWidget(nullableComponent
|
.setWidget(nullableComponent
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
|
|||||||
|
|
||||||
|
|
||||||
public WorkspaceItemInfo() {
|
public WorkspaceItemInfo() {
|
||||||
itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorkspaceItemInfo(WorkspaceItemInfo info) {
|
public WorkspaceItemInfo(WorkspaceItemInfo info) {
|
||||||
@@ -205,8 +205,8 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
|
|||||||
@Override
|
@Override
|
||||||
public ComponentName getTargetComponent() {
|
public ComponentName getTargetComponent() {
|
||||||
ComponentName cn = super.getTargetComponent();
|
ComponentName cn = super.getTargetComponent();
|
||||||
if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT || hasStatusFlag(
|
if (cn == null && hasStatusFlag(
|
||||||
FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON))) {
|
FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON)) {
|
||||||
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
|
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
|
||||||
// a packageName. In that case create a empty componentName instead of adding additional
|
// a packageName. In that case create a empty componentName instead of adding additional
|
||||||
// check everywhere.
|
// check everywhere.
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAn
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getItemType() {
|
public int getItemType() {
|
||||||
return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
return LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -250,6 +250,11 @@ public class IntArray implements Cloneable, Iterable<Integer> {
|
|||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "IntArray [" + toConcatString() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
public static IntArray fromConcatString(String concatString) {
|
public static IntArray fromConcatString(String concatString) {
|
||||||
StringTokenizer tokenizer = new StringTokenizer(concatString, ",");
|
StringTokenizer tokenizer = new StringTokenizer(concatString, ",");
|
||||||
int[] array = new int[tokenizer.countTokens()];
|
int[] array = new int[tokenizer.countTokens()];
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ public class MainThreadInitializedObject<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public T get(Context context) {
|
public T get(Context context) {
|
||||||
if (context instanceof SandboxContext) {
|
if (context instanceof SandboxContext sc) {
|
||||||
return ((SandboxContext) context).getObject(this, mProvider);
|
return sc.getObject(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mValue == null) {
|
if (mValue == null) {
|
||||||
@@ -131,24 +131,22 @@ public class MainThreadInitializedObject<T> {
|
|||||||
* Find a cached object from mObjectMap if we have already created one. If not, generate
|
* Find a cached object from mObjectMap if we have already created one. If not, generate
|
||||||
* an object using the provider.
|
* an object using the provider.
|
||||||
*/
|
*/
|
||||||
protected <T> T getObject(MainThreadInitializedObject<T> object,
|
protected <T> T getObject(MainThreadInitializedObject<T> object) {
|
||||||
ObjectProvider<T> provider) {
|
|
||||||
synchronized (mDestroyLock) {
|
synchronized (mDestroyLock) {
|
||||||
if (mDestroyed) {
|
if (mDestroyed) {
|
||||||
Log.e(TAG, "Static object access with a destroyed context");
|
Log.e(TAG, "Static object access with a destroyed context");
|
||||||
}
|
}
|
||||||
|
|
||||||
T t = (T) mObjectMap.get(object);
|
T t = (T) mObjectMap.get(object);
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||||
t = createObject(provider);
|
t = createObject(object);
|
||||||
// Check if we've explicitly allowed the object or if it's a SafeCloseable,
|
// Check if we've explicitly allowed the object or if it's a SafeCloseable,
|
||||||
// it will get destroyed in onDestroy()
|
// it will get destroyed in onDestroy()
|
||||||
if (!mAllowedObjects.contains(object) && !(t instanceof SafeCloseable)) {
|
if (!mAllowedObjects.contains(object) && !(t instanceof SafeCloseable)) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException("Leaking unknown objects "
|
||||||
"Leaking unknown objects " + object + " " + provider + " " + t);
|
+ object + " " + object.mProvider + " " + t);
|
||||||
}
|
}
|
||||||
mObjectMap.put(object, t);
|
mObjectMap.put(object, t);
|
||||||
mOrderedObjects.add(t);
|
mOrderedObjects.add(t);
|
||||||
@@ -157,15 +155,15 @@ public class MainThreadInitializedObject<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return MAIN_EXECUTOR.submit(() -> getObject(object, provider)).get();
|
return MAIN_EXECUTOR.submit(() -> getObject(object)).get();
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
protected <T> T createObject(ObjectProvider<T> provider) {
|
protected <T> T createObject(MainThreadInitializedObject<T> object) {
|
||||||
return provider.get(this);
|
return object.mProvider.get(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -332,6 +332,12 @@ public interface ActivityContext {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isShortcut = (item instanceof WorkspaceItemInfo)
|
||||||
|
&& item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
|
||||||
|
&& !((WorkspaceItemInfo) item).isPromise();
|
||||||
|
if (isShortcut && GO_DISABLE_WIDGETS) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
|
ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
|
||||||
: makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON
|
: makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON
|
||||||
? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */);
|
? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */);
|
||||||
@@ -343,13 +349,11 @@ public interface ActivityContext {
|
|||||||
intent.setSourceBounds(Utilities.getViewBounds(v));
|
intent.setSourceBounds(Utilities.getViewBounds(v));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
boolean isShortcut = (item instanceof WorkspaceItemInfo)
|
|
||||||
&& (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
|
|
||||||
|| item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
|
|
||||||
&& !((WorkspaceItemInfo) item).isPromise();
|
|
||||||
if (isShortcut) {
|
if (isShortcut) {
|
||||||
// Shortcuts need some special checks due to legacy reasons.
|
String id = ((WorkspaceItemInfo) item).getDeepShortcutId();
|
||||||
startShortcutIntentSafely(intent, optsBundle, item);
|
String packageName = intent.getPackage();
|
||||||
|
((Context) this).getSystemService(LauncherApps.class).startShortcut(
|
||||||
|
packageName, id, intent.getSourceBounds(), optsBundle, user);
|
||||||
} else if (user == null || user.equals(Process.myUserHandle())) {
|
} else if (user == null || user.equals(Process.myUserHandle())) {
|
||||||
// Could be launching some bookkeeping activity
|
// Could be launching some bookkeeping activity
|
||||||
context.startActivity(intent, optsBundle);
|
context.startActivity(intent, optsBundle);
|
||||||
@@ -424,55 +428,6 @@ public interface ActivityContext {
|
|||||||
return new ActivityOptionsWrapper(options, new RunnableList());
|
return new ActivityOptionsWrapper(options, new RunnableList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Safely launches an intent for a shortcut.
|
|
||||||
*
|
|
||||||
* @param intent Intent to start.
|
|
||||||
* @param optsBundle Optional launch arguments.
|
|
||||||
* @param info Shortcut information.
|
|
||||||
*/
|
|
||||||
default void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
|
|
||||||
try {
|
|
||||||
StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
|
|
||||||
try {
|
|
||||||
// Temporarily disable deathPenalty on all default checks. For eg, shortcuts
|
|
||||||
// containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
|
|
||||||
// is enabled by default on NYC.
|
|
||||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
|
|
||||||
.penaltyLog().build());
|
|
||||||
|
|
||||||
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
|
|
||||||
String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
|
|
||||||
String packageName = intent.getPackage();
|
|
||||||
startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
|
|
||||||
} else {
|
|
||||||
// Could be launching some bookkeeping activity
|
|
||||||
((Context) this).startActivity(intent, optsBundle);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
StrictMode.setVmPolicy(oldPolicy);
|
|
||||||
}
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper around the platform method with Launcher specific checks.
|
|
||||||
*/
|
|
||||||
default void startShortcut(String packageName, String id, Rect sourceBounds,
|
|
||||||
Bundle startActivityOptions, UserHandle user) {
|
|
||||||
if (GO_DISABLE_WIDGETS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
((Context) this).getSystemService(LauncherApps.class).startShortcut(packageName, id,
|
|
||||||
sourceBounds, startActivityOptions, user);
|
|
||||||
} catch (SecurityException | IllegalStateException e) {
|
|
||||||
Log.e(TAG, "Failed to start shortcut", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default CellPosMapper getCellPosMapper() {
|
default CellPosMapper getCellPosMapper() {
|
||||||
return CellPosMapper.DEFAULT;
|
return CellPosMapper.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,7 +290,6 @@ public class OptionsPopupView extends ArrowPopup<Launcher>
|
|||||||
static WorkspaceItemInfo placeholderInfo(Intent intent) {
|
static WorkspaceItemInfo placeholderInfo(Intent intent) {
|
||||||
WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo();
|
WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo();
|
||||||
placeholderInfo.intent = intent;
|
placeholderInfo.intent = intent;
|
||||||
placeholderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
|
||||||
placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS;
|
placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS;
|
||||||
return placeholderInfo;
|
return placeholderInfo;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
# Model data used by CacheDataUpdatedTaskTest
|
|
||||||
|
|
||||||
classMap s com.android.launcher3.model.data.WorkspaceItemInfo
|
|
||||||
|
|
||||||
# Items for the BgDataModel
|
|
||||||
|
|
||||||
# App shortcuts
|
|
||||||
bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
|
|
||||||
bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
|
|
||||||
bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
|
|
||||||
bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
|
|
||||||
|
|
||||||
# Auto install app shortcut
|
|
||||||
bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
|
|
||||||
bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
|
|
||||||
|
|
||||||
# Custom shortcuts
|
|
||||||
bgItem s itemType=1 title=app1-shrt intent=component=app1/class3 id=7
|
|
||||||
bgItem s itemType=1 title=app4-shrt intent=component=app4/class1 id=8
|
|
||||||
|
|
||||||
# Restored custom shortcut
|
|
||||||
bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=9
|
|
||||||
bgItem s itemType=1 status=1 title=app5-shrt intent=component=app5/class1 id=10
|
|
||||||
|
|
||||||
allApps componentName=app1/class1 intent=component=app1/class1
|
|
||||||
allApps componentName=app1/class2 intent=component=app1/class2
|
|
||||||
allApps componentName=app2/class1 intent=component=app2/class1
|
|
||||||
allApps componentName=app2/class2 intent=component=app2/class2
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Model data used by PackageInstallStateChangeTaskTest
|
|
||||||
|
|
||||||
classMap s com.android.launcher3.model.data.WorkspaceItemInfo
|
|
||||||
classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
|
|
||||||
|
|
||||||
# Items for the BgDataModel
|
|
||||||
|
|
||||||
# App shortcuts
|
|
||||||
bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
|
|
||||||
bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
|
|
||||||
bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
|
|
||||||
bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
|
|
||||||
|
|
||||||
# Promise icons for app3
|
|
||||||
bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
|
|
||||||
bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
|
|
||||||
bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7
|
|
||||||
|
|
||||||
# Promise icon for app4
|
|
||||||
bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8
|
|
||||||
|
|
||||||
# Widget
|
|
||||||
bgItem w providerName=app4/provider1 id=9
|
|
||||||
bgItem w providerName=app5/provider1 id=10
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Model data used by WidgetsPredictionUpdateTasksTest
|
|
||||||
|
|
||||||
classMap s com.android.launcher3.model.data.WorkspaceItemInfo
|
|
||||||
classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
|
|
||||||
|
|
||||||
# Items for the BgDataModel
|
|
||||||
|
|
||||||
# App shortcuts
|
|
||||||
bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
|
|
||||||
bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
|
|
||||||
bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
|
|
||||||
bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
|
|
||||||
|
|
||||||
# Promise icons for app3
|
|
||||||
bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
|
|
||||||
bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
|
|
||||||
bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7
|
|
||||||
|
|
||||||
# Promise icon for app4
|
|
||||||
bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8
|
|
||||||
|
|
||||||
# Widget
|
|
||||||
bgItem w providerName=app4/provider1 id=9
|
|
||||||
bgItem w providerName=app5/provider1 id=10
|
|
||||||
@@ -17,17 +17,18 @@ package com.android.launcher3.model
|
|||||||
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import com.android.launcher3.InvariantDeviceProfile
|
import com.android.launcher3.InvariantDeviceProfile
|
||||||
import com.android.launcher3.LauncherAppState
|
import com.android.launcher3.LauncherAppState
|
||||||
import com.android.launcher3.LauncherSettings
|
import com.android.launcher3.model.data.AppInfo
|
||||||
import com.android.launcher3.model.data.WorkspaceItemInfo
|
import com.android.launcher3.model.data.WorkspaceItemInfo
|
||||||
import com.android.launcher3.util.ContentWriter
|
|
||||||
import com.android.launcher3.util.GridOccupancy
|
import com.android.launcher3.util.GridOccupancy
|
||||||
import com.android.launcher3.util.IntArray
|
import com.android.launcher3.util.IntArray
|
||||||
import com.android.launcher3.util.IntSparseArrayMap
|
import com.android.launcher3.util.IntSparseArrayMap
|
||||||
|
import com.android.launcher3.util.LauncherLayoutBuilder
|
||||||
import com.android.launcher3.util.LauncherModelHelper
|
import com.android.launcher3.util.LauncherModelHelper
|
||||||
|
import com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY
|
||||||
|
import com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
/** Base class for workspace related tests. */
|
/** Base class for workspace related tests. */
|
||||||
@@ -38,6 +39,7 @@ abstract class AbstractWorkspaceModelTest {
|
|||||||
val nonEmptyScreenSpaces = listOf(Rect(1, 2, 3, 4))
|
val nonEmptyScreenSpaces = listOf(Rect(1, 2, 3, 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected lateinit var mLayoutBuilder: LauncherLayoutBuilder
|
||||||
protected lateinit var mTargetContext: Context
|
protected lateinit var mTargetContext: Context
|
||||||
protected lateinit var mIdp: InvariantDeviceProfile
|
protected lateinit var mIdp: InvariantDeviceProfile
|
||||||
protected lateinit var mAppState: LauncherAppState
|
protected lateinit var mAppState: LauncherAppState
|
||||||
@@ -47,6 +49,7 @@ abstract class AbstractWorkspaceModelTest {
|
|||||||
protected lateinit var mScreenOccupancy: IntSparseArrayMap<GridOccupancy>
|
protected lateinit var mScreenOccupancy: IntSparseArrayMap<GridOccupancy>
|
||||||
|
|
||||||
open fun setup() {
|
open fun setup() {
|
||||||
|
mLayoutBuilder = LauncherLayoutBuilder()
|
||||||
mModelHelper = LauncherModelHelper()
|
mModelHelper = LauncherModelHelper()
|
||||||
mTargetContext = mModelHelper.sandboxContext
|
mTargetContext = mModelHelper.sandboxContext
|
||||||
mIdp = InvariantDeviceProfile.INSTANCE[mTargetContext]
|
mIdp = InvariantDeviceProfile.INSTANCE[mTargetContext]
|
||||||
@@ -64,10 +67,11 @@ abstract class AbstractWorkspaceModelTest {
|
|||||||
|
|
||||||
/** Sets up workspaces with the given screen IDs with some items and a 2x2 space. */
|
/** Sets up workspaces with the given screen IDs with some items and a 2x2 space. */
|
||||||
fun setupWorkspaces(screenIdsWithItems: List<Int>) {
|
fun setupWorkspaces(screenIdsWithItems: List<Int>) {
|
||||||
var nextItemId = 1
|
screenIdsWithItems.forEach { screenId -> setupWorkspace(screenId, nonEmptyScreenSpaces) }
|
||||||
screenIdsWithItems.forEach { screenId ->
|
mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder)
|
||||||
nextItemId = setupWorkspace(nextItemId, screenId, nonEmptyScreenSpaces)
|
mIdp.numRows = 5
|
||||||
}
|
mIdp.numColumns = mIdp.numRows
|
||||||
|
mModelHelper.loadModelSync()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,30 +82,23 @@ abstract class AbstractWorkspaceModelTest {
|
|||||||
screen1: List<Rect>? = null,
|
screen1: List<Rect>? = null,
|
||||||
screen2: List<Rect>? = null,
|
screen2: List<Rect>? = null,
|
||||||
screen3: List<Rect>? = null,
|
screen3: List<Rect>? = null,
|
||||||
) = listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces)
|
) {
|
||||||
|
listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces)
|
||||||
|
mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder)
|
||||||
|
mIdp.numRows = 5
|
||||||
|
mIdp.numColumns = mIdp.numRows
|
||||||
|
mModelHelper.loadModelSync()
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupWithSpaces(workspaceSpaces: List<List<Rect>?>) {
|
private fun setupWithSpaces(workspaceSpaces: List<List<Rect>?>) {
|
||||||
var nextItemId = 1
|
|
||||||
workspaceSpaces.forEachIndexed { screenId, spaces ->
|
workspaceSpaces.forEachIndexed { screenId, spaces ->
|
||||||
if (spaces != null) {
|
if (spaces != null) {
|
||||||
nextItemId = setupWorkspace(nextItemId, screenId, spaces)
|
setupWorkspace(screenId, spaces)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupWorkspace(startId: Int, screenId: Int, spaces: List<Rect>): Int {
|
private fun setupWorkspace(screenId: Int, spaces: List<Rect>) {
|
||||||
return mModelHelper.executeSimpleTask { dataModel ->
|
|
||||||
writeWorkspaceWithSpaces(dataModel, startId, screenId, spaces)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeWorkspaceWithSpaces(
|
|
||||||
bgDataModel: BgDataModel,
|
|
||||||
itemStartId: Int,
|
|
||||||
screenId: Int,
|
|
||||||
spaces: List<Rect>,
|
|
||||||
): Int {
|
|
||||||
var itemId = itemStartId
|
|
||||||
val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows)
|
val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows)
|
||||||
occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true)
|
occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true)
|
||||||
spaces.forEach { spaceRect -> occupancy.markCells(spaceRect, false) }
|
spaces.forEach { spaceRect -> occupancy.markCells(spaceRect, false) }
|
||||||
@@ -109,35 +106,22 @@ abstract class AbstractWorkspaceModelTest {
|
|||||||
mScreenOccupancy.append(screenId, occupancy)
|
mScreenOccupancy.append(screenId, occupancy)
|
||||||
for (x in 0 until mIdp.numColumns) {
|
for (x in 0 until mIdp.numColumns) {
|
||||||
for (y in 0 until mIdp.numRows) {
|
for (y in 0 until mIdp.numRows) {
|
||||||
if (!occupancy.cells[x][y]) {
|
if (occupancy.cells[x][y]) {
|
||||||
continue
|
mLayoutBuilder.atWorkspace(x, y, screenId).putApp(TEST_PACKAGE, TEST_ACTIVITY)
|
||||||
}
|
}
|
||||||
val info = getExistingItem()
|
|
||||||
info.id = itemId++
|
|
||||||
info.screenId = screenId
|
|
||||||
info.cellX = x
|
|
||||||
info.cellY = y
|
|
||||||
info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP
|
|
||||||
bgDataModel.addItem(mTargetContext, info, false)
|
|
||||||
val writer = ContentWriter(mTargetContext)
|
|
||||||
info.writeToValues(writer)
|
|
||||||
writer.put(LauncherSettings.Favorites._ID, info.id)
|
|
||||||
mTargetContext.contentResolver.insert(
|
|
||||||
LauncherSettings.Favorites.CONTENT_URI,
|
|
||||||
writer.getValues(mTargetContext)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return itemId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getExistingItem() =
|
fun getExistingItem() =
|
||||||
WorkspaceItemInfo().apply { intent = Intent().setComponent(ComponentName("a", "b")) }
|
WorkspaceItemInfo().apply {
|
||||||
|
intent = AppInfo.makeLaunchIntent(ComponentName(TEST_PACKAGE, TEST_ACTIVITY))
|
||||||
|
}
|
||||||
|
|
||||||
fun getNewItem(): WorkspaceItemInfo {
|
fun getNewItem(): WorkspaceItemInfo {
|
||||||
val itemPackage = UUID.randomUUID().toString()
|
val itemPackage = UUID.randomUUID().toString()
|
||||||
return WorkspaceItemInfo().apply {
|
return WorkspaceItemInfo().apply {
|
||||||
intent = Intent().setComponent(ComponentName(itemPackage, itemPackage))
|
intent = AppInfo.makeLaunchIntent(ComponentName(itemPackage, itemPackage))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import com.android.launcher3.model.data.ItemInfo
|
|||||||
import com.android.launcher3.model.data.WorkspaceItemInfo
|
import com.android.launcher3.model.data.WorkspaceItemInfo
|
||||||
import com.android.launcher3.util.Executors
|
import com.android.launcher3.util.Executors
|
||||||
import com.android.launcher3.util.IntArray
|
import com.android.launcher3.util.IntArray
|
||||||
import com.android.launcher3.util.IntSet
|
import com.android.launcher3.util.TestUtil.runOnExecutorSync
|
||||||
import com.android.launcher3.util.any
|
import com.android.launcher3.util.any
|
||||||
import com.android.launcher3.util.eq
|
import com.android.launcher3.util.eq
|
||||||
import com.android.launcher3.util.same
|
import com.android.launcher3.util.same
|
||||||
@@ -32,8 +32,6 @@ import org.junit.After
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.ArgumentCaptor
|
|
||||||
import org.mockito.Captor
|
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.times
|
import org.mockito.Mockito.times
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
@@ -46,11 +44,7 @@ import org.mockito.MockitoAnnotations
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
|
class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
|
||||||
|
|
||||||
@Captor private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
|
private lateinit var mDataModelCallbacks: MyCallbacks
|
||||||
|
|
||||||
@Captor private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
|
|
||||||
|
|
||||||
@Mock private lateinit var mDataModelCallbacks: BgDataModel.Callbacks
|
|
||||||
|
|
||||||
@Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
|
@Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
|
||||||
|
|
||||||
@@ -58,7 +52,7 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
|
|||||||
override fun setup() {
|
override fun setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
whenever(mDataModelCallbacks.getPagesToBindSynchronously(any())).thenReturn(IntSet())
|
mDataModelCallbacks = MyCallbacks()
|
||||||
Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) }
|
Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) }
|
||||||
.get()
|
.get()
|
||||||
}
|
}
|
||||||
@@ -105,7 +99,7 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
|
|||||||
val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
|
val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
|
||||||
|
|
||||||
assertThat(addedItems.size).isEqualTo(0)
|
assertThat(addedItems.size).isEqualTo(0)
|
||||||
verifyZeroInteractions(mWorkspaceItemSpaceFinder, mDataModelCallbacks)
|
verifyZeroInteractions(mWorkspaceItemSpaceFinder)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -191,22 +185,14 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
|
|||||||
): List<AddedItem> {
|
): List<AddedItem> {
|
||||||
setupWorkspaces(nonEmptyScreenIds)
|
setupWorkspaces(nonEmptyScreenIds)
|
||||||
val task = newTask(*itemsToAdd)
|
val task = newTask(*itemsToAdd)
|
||||||
var updateCount = 0
|
|
||||||
mModelHelper.executeTaskForTest(task).forEach {
|
|
||||||
updateCount++
|
|
||||||
it.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
val addedItems = mutableListOf<AddedItem>()
|
val addedItems = mutableListOf<AddedItem>()
|
||||||
if (updateCount > 0) {
|
|
||||||
verify(mDataModelCallbacks)
|
runOnExecutorSync(Executors.MODEL_EXECUTOR) {
|
||||||
.bindAppsAdded(
|
mDataModelCallbacks.addedItems.clear()
|
||||||
any(),
|
mModelHelper.model.enqueueModelUpdateTask(task)
|
||||||
mNotAnimatedItemArgumentCaptor.capture(),
|
runOnExecutorSync(Executors.MAIN_EXECUTOR) {}
|
||||||
mAnimatedItemArgumentCaptor.capture()
|
addedItems.addAll(mDataModelCallbacks.addedItems)
|
||||||
)
|
|
||||||
addedItems.addAll(mAnimatedItemArgumentCaptor.value.map { AddedItem(it, true) })
|
|
||||||
addedItems.addAll(mNotAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return addedItems
|
return addedItems
|
||||||
@@ -224,3 +210,17 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean)
|
private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean)
|
||||||
|
|
||||||
|
private class MyCallbacks : BgDataModel.Callbacks {
|
||||||
|
|
||||||
|
val addedItems = mutableListOf<AddedItem>()
|
||||||
|
|
||||||
|
override fun bindAppsAdded(
|
||||||
|
newScreens: IntArray?,
|
||||||
|
addNotAnimated: ArrayList<ItemInfo>,
|
||||||
|
addAnimated: ArrayList<ItemInfo>
|
||||||
|
) {
|
||||||
|
addedItems.addAll(addAnimated.map { AddedItem(it, true) })
|
||||||
|
addedItems.addAll(addNotAnimated.map { AddedItem(it, false) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,32 +1,31 @@
|
|||||||
package com.android.launcher3.model;
|
package com.android.launcher3.model;
|
||||||
|
|
||||||
|
import static android.os.Process.myUserHandle;
|
||||||
|
|
||||||
|
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
|
||||||
|
import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotSame;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Bitmap.Config;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Process;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.os.UserManager;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.icons.BitmapInfo;
|
import com.android.launcher3.icons.BitmapInfo;
|
||||||
import com.android.launcher3.icons.IconCache;
|
import com.android.launcher3.model.data.FolderInfo;
|
||||||
import com.android.launcher3.icons.cache.CachingLogic;
|
|
||||||
import com.android.launcher3.model.data.AppInfo;
|
|
||||||
import com.android.launcher3.model.data.ItemInfo;
|
|
||||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||||
|
import com.android.launcher3.util.IntSet;
|
||||||
|
import com.android.launcher3.util.LauncherLayoutBuilder;
|
||||||
import com.android.launcher3.util.LauncherModelHelper;
|
import com.android.launcher3.util.LauncherModelHelper;
|
||||||
|
import com.android.launcher3.util.PackageUserKey;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -35,6 +34,7 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link CacheDataUpdatedTask}
|
* Tests for {@link CacheDataUpdatedTask}
|
||||||
@@ -43,49 +43,40 @@ import java.util.HashSet;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class CacheDataUpdatedTaskTest {
|
public class CacheDataUpdatedTaskTest {
|
||||||
|
|
||||||
private static final String NEW_LABEL_PREFIX = "new-label-";
|
private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
|
||||||
|
private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
|
||||||
|
|
||||||
private LauncherModelHelper mModelHelper;
|
private LauncherModelHelper mModelHelper;
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
private int mSession1;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
mModelHelper = new LauncherModelHelper();
|
mModelHelper = new LauncherModelHelper();
|
||||||
mModelHelper.initializeData("cache_data_updated_task_data");
|
mContext = mModelHelper.sandboxContext;
|
||||||
|
mSession1 = mModelHelper.createInstallerSession(PENDING_APP_1);
|
||||||
|
mModelHelper.createInstallerSession(PENDING_APP_2);
|
||||||
|
|
||||||
// Add placeholder entries in the cache to simulate update
|
LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
|
||||||
Context context = mModelHelper.sandboxContext;
|
.atHotseat(1).putFolder("MyFolder")
|
||||||
IconCache iconCache = LauncherAppState.getInstance(context).getIconCache();
|
.addApp(TEST_PACKAGE, TEST_ACTIVITY) // 2
|
||||||
CachingLogic<ItemInfo> placeholderLogic = new CachingLogic<ItemInfo>() {
|
.addApp(TEST_PACKAGE, TEST_ACTIVITY2) // 3
|
||||||
@Override
|
.addApp(TEST_PACKAGE, TEST_ACTIVITY3) // 4
|
||||||
@NonNull
|
|
||||||
public ComponentName getComponent(@NonNull ItemInfo info) {
|
|
||||||
return info.getTargetComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
// Pending App 1
|
||||||
@Override
|
.addApp(PENDING_APP_1, TEST_ACTIVITY) // 5
|
||||||
public UserHandle getUser(@NonNull ItemInfo info) {
|
.addApp(PENDING_APP_1, TEST_ACTIVITY2) // 6
|
||||||
return info.user;
|
.addApp(PENDING_APP_1, TEST_ACTIVITY3) // 7
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
// Pending App 2
|
||||||
@Override
|
.addApp(PENDING_APP_2, TEST_ACTIVITY) // 8
|
||||||
public CharSequence getLabel(@NonNull ItemInfo info) {
|
.addApp(PENDING_APP_2, TEST_ACTIVITY2) // 9
|
||||||
return NEW_LABEL_PREFIX + info.id;
|
.addApp(PENDING_APP_2, TEST_ACTIVITY3) // 10
|
||||||
}
|
.build();
|
||||||
|
mModelHelper.setupDefaultLayoutProvider(builder);
|
||||||
@NonNull
|
mModelHelper.loadModelSync();
|
||||||
@Override
|
assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size());
|
||||||
public BitmapInfo loadIcon(@NonNull Context context, @NonNull ItemInfo info) {
|
|
||||||
return BitmapInfo.of(Bitmap.createBitmap(1, 1, Config.ARGB_8888), Color.RED);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
UserManager um = context.getSystemService(UserManager.class);
|
|
||||||
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
|
|
||||||
iconCache.addIconToDBAndMemCache(info, placeholderLogic, new PackageInfo(),
|
|
||||||
um.getSerialNumberForUser(info.user), true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -94,27 +85,63 @@ public class CacheDataUpdatedTaskTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CacheDataUpdatedTask newTask(int op, String... pkg) {
|
private CacheDataUpdatedTask newTask(int op, String... pkg) {
|
||||||
return new CacheDataUpdatedTask(op, Process.myUserHandle(),
|
return new CacheDataUpdatedTask(op, myUserHandle(),
|
||||||
new HashSet<>(Arrays.asList(pkg)));
|
new HashSet<>(Arrays.asList(pkg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCacheUpdate_update_apps() throws Exception {
|
public void testCacheUpdate_update_apps() {
|
||||||
// Clear all icons from apps list so that its easy to check what was updated
|
// Run on model executor so that no other task runs in the middle.
|
||||||
for (AppInfo info : mModelHelper.getAllAppsList().data) {
|
runOnExecutorSync(MODEL_EXECUTOR, () -> {
|
||||||
info.bitmap = BitmapInfo.LOW_RES_INFO;
|
// Clear all icons from apps list so that its easy to check what was updated
|
||||||
}
|
allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
|
||||||
|
|
||||||
mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
|
mModelHelper.getModel().enqueueModelUpdateTask(
|
||||||
|
newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, TEST_PACKAGE));
|
||||||
|
|
||||||
// Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7)
|
// Verify that only the app icons of TEST_PACKAGE (id 2, 3, 4) are updated.
|
||||||
// is not updated
|
verifyUpdate(2, 3, 4);
|
||||||
verifyUpdate(1, 2);
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that only app1 var updated in allAppsList
|
@Test
|
||||||
assertFalse(mModelHelper.getAllAppsList().data.isEmpty());
|
public void testSessionUpdate_ignores_normal_apps() {
|
||||||
for (AppInfo info : mModelHelper.getAllAppsList().data) {
|
// Run on model executor so that no other task runs in the middle.
|
||||||
if (info.componentName.getPackageName().equals("app1")) {
|
runOnExecutorSync(MODEL_EXECUTOR, () -> {
|
||||||
|
// Clear all icons from apps list so that its easy to check what was updated
|
||||||
|
allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
|
||||||
|
|
||||||
|
mModelHelper.getModel().enqueueModelUpdateTask(
|
||||||
|
newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, TEST_PACKAGE));
|
||||||
|
|
||||||
|
// TEST_PACKAGE has no restored shortcuts. Verify that nothing was updated.
|
||||||
|
verifyUpdate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSessionUpdate_updates_pending_apps() {
|
||||||
|
// Run on model executor so that no other task runs in the middle.
|
||||||
|
runOnExecutorSync(MODEL_EXECUTOR, () -> {
|
||||||
|
LauncherAppState.getInstance(mContext).getIconCache().updateSessionCache(
|
||||||
|
new PackageUserKey(PENDING_APP_1, myUserHandle()),
|
||||||
|
mContext.getPackageManager().getPackageInstaller().getSessionInfo(mSession1));
|
||||||
|
|
||||||
|
// Clear all icons from apps list so that its easy to check what was updated
|
||||||
|
allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
|
||||||
|
|
||||||
|
mModelHelper.getModel().enqueueModelUpdateTask(
|
||||||
|
newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, PENDING_APP_1));
|
||||||
|
|
||||||
|
// Only restored apps from PENDING_APP_1 (id 5, 6, 7) are updated
|
||||||
|
verifyUpdate(5, 6, 7);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyUpdate(int... idsUpdated) {
|
||||||
|
IntSet updates = IntSet.wrap(idsUpdated);
|
||||||
|
for (WorkspaceItemInfo info : allItems()) {
|
||||||
|
if (updates.contains(info.id)) {
|
||||||
assertFalse(info.bitmap.isNullOrLowRes());
|
assertFalse(info.bitmap.isNullOrLowRes());
|
||||||
} else {
|
} else {
|
||||||
assertTrue(info.bitmap.isNullOrLowRes());
|
assertTrue(info.bitmap.isNullOrLowRes());
|
||||||
@@ -122,33 +149,7 @@ public class CacheDataUpdatedTaskTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private List<WorkspaceItemInfo> allItems() {
|
||||||
public void testSessionUpdate_ignores_normal_apps() throws Exception {
|
return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).contents;
|
||||||
mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
|
|
||||||
|
|
||||||
// app1 has no restored shortcuts. Verify that nothing was updated.
|
|
||||||
verifyUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSessionUpdate_updates_pending_apps() throws Exception {
|
|
||||||
mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
|
|
||||||
|
|
||||||
// app3 has only restored apps (id 5, 6) and shortcuts (id 9). Verify that only apps were
|
|
||||||
// were updated
|
|
||||||
verifyUpdate(5, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verifyUpdate(Integer... idsUpdated) {
|
|
||||||
HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
|
|
||||||
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
|
|
||||||
if (updates.contains(info.id)) {
|
|
||||||
assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
|
|
||||||
assertFalse(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
|
|
||||||
} else {
|
|
||||||
assertNotSame(NEW_LABEL_PREFIX + info.id, info.title);
|
|
||||||
assertTrue(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import android.graphics.Point
|
|||||||
import android.os.Process
|
import android.os.Process
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
|
||||||
import com.android.launcher3.InvariantDeviceProfile
|
import com.android.launcher3.InvariantDeviceProfile
|
||||||
import com.android.launcher3.LauncherPrefs
|
import com.android.launcher3.LauncherPrefs
|
||||||
import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
|
import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
|
||||||
@@ -108,8 +107,8 @@ class GridSizeMigrationUtilTest {
|
|||||||
fun testMigration() {
|
fun testMigration() {
|
||||||
// Src Hotseat icons
|
// Src Hotseat icons
|
||||||
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
|
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
|
||||||
addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
|
addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
|
||||||
addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
|
addItem(ITEM_TYPE_DEEP_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
|
||||||
addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
|
addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
|
||||||
// Src grid icons
|
// Src grid icons
|
||||||
// _ _ _ _ _
|
// _ _ _ _ _
|
||||||
@@ -124,7 +123,7 @@ class GridSizeMigrationUtilTest {
|
|||||||
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 3, testPackage9, 9, TMP_TABLE)
|
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 3, testPackage9, 9, TMP_TABLE)
|
||||||
|
|
||||||
// Dest hotseat icons
|
// Dest hotseat icons
|
||||||
addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2)
|
addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2)
|
||||||
// Dest grid icons
|
// Dest grid icons
|
||||||
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage10)
|
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage10)
|
||||||
|
|
||||||
@@ -219,8 +218,8 @@ class GridSizeMigrationUtilTest {
|
|||||||
// Hotseat items in grid A
|
// Hotseat items in grid A
|
||||||
// 1 2 _ 3 4
|
// 1 2 _ 3 4
|
||||||
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
|
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
|
||||||
addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
|
addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
|
||||||
addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
|
addItem(ITEM_TYPE_DEEP_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
|
||||||
addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
|
addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
|
||||||
// Workspace items in grid A
|
// Workspace items in grid A
|
||||||
// _ _ _ _ _
|
// _ _ _ _ _
|
||||||
@@ -235,7 +234,7 @@ class GridSizeMigrationUtilTest {
|
|||||||
|
|
||||||
// Hotseat items in grid B
|
// Hotseat items in grid B
|
||||||
// 2 _ _ _
|
// 2 _ _ _
|
||||||
addItem(ITEM_TYPE_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2)
|
addItem(ITEM_TYPE_DEEP_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2)
|
||||||
// Workspace items in grid B
|
// Workspace items in grid B
|
||||||
// _ _ _ _
|
// _ _ _ _
|
||||||
// _ _ _ 10
|
// _ _ _ 10
|
||||||
@@ -291,7 +290,7 @@ class GridSizeMigrationUtilTest {
|
|||||||
null
|
null
|
||||||
)
|
)
|
||||||
?: throw IllegalStateException()
|
?: throw IllegalStateException()
|
||||||
var locMap = parseLocMap(context, c)
|
var locMap = parseLocMap(c)
|
||||||
// Expected items in grid B
|
// Expected items in grid B
|
||||||
// _ _ _ _
|
// _ _ _ _
|
||||||
// 5 6 7 8
|
// 5 6 7 8
|
||||||
@@ -348,7 +347,7 @@ class GridSizeMigrationUtilTest {
|
|||||||
null
|
null
|
||||||
)
|
)
|
||||||
?: throw IllegalStateException()
|
?: throw IllegalStateException()
|
||||||
locMap = parseLocMap(context, c)
|
locMap = parseLocMap(c)
|
||||||
// Expected workspace items in grid A
|
// Expected workspace items in grid A
|
||||||
// _ _ _ _ _
|
// _ _ _ _ _
|
||||||
// _ _ _ _ 5
|
// _ _ _ _ 5
|
||||||
@@ -410,7 +409,7 @@ class GridSizeMigrationUtilTest {
|
|||||||
null
|
null
|
||||||
)
|
)
|
||||||
?: throw IllegalStateException()
|
?: throw IllegalStateException()
|
||||||
locMap = parseLocMap(context, c)
|
locMap = parseLocMap(c)
|
||||||
// Expected workspace items in grid B
|
// Expected workspace items in grid B
|
||||||
// _ _ _ _
|
// _ _ _ _
|
||||||
// 5 6 _ 8
|
// 5 6 _ 8
|
||||||
@@ -436,7 +435,7 @@ class GridSizeMigrationUtilTest {
|
|||||||
c.close()
|
c.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseLocMap(context: Context, c: Cursor): Map<String, Triple<Int, Int, Int>> {
|
private fun parseLocMap(c: Cursor): Map<String, Triple<Int, Int, Int>> {
|
||||||
// Check workspace items
|
// Check workspace items
|
||||||
val intentIndex = c.getColumnIndex(INTENT)
|
val intentIndex = c.getColumnIndex(INTENT)
|
||||||
val screenIndex = c.getColumnIndex(SCREEN)
|
val screenIndex = c.getColumnIndex(SCREEN)
|
||||||
@@ -465,7 +464,16 @@ class GridSizeMigrationUtilTest {
|
|||||||
1,
|
1,
|
||||||
TMP_TABLE
|
TMP_TABLE
|
||||||
),
|
),
|
||||||
addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE),
|
addItem(
|
||||||
|
ITEM_TYPE_DEEP_SHORTCUT,
|
||||||
|
1,
|
||||||
|
CONTAINER_HOTSEAT,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
testPackage2,
|
||||||
|
2,
|
||||||
|
TMP_TABLE
|
||||||
|
),
|
||||||
addItem(
|
addItem(
|
||||||
ITEM_TYPE_APPLICATION,
|
ITEM_TYPE_APPLICATION,
|
||||||
2,
|
2,
|
||||||
@@ -476,7 +484,16 @@ class GridSizeMigrationUtilTest {
|
|||||||
3,
|
3,
|
||||||
TMP_TABLE
|
TMP_TABLE
|
||||||
),
|
),
|
||||||
addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
|
addItem(
|
||||||
|
ITEM_TYPE_DEEP_SHORTCUT,
|
||||||
|
3,
|
||||||
|
CONTAINER_HOTSEAT,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
testPackage4,
|
||||||
|
4,
|
||||||
|
TMP_TABLE
|
||||||
|
)
|
||||||
)
|
)
|
||||||
val numSrcDatabaseHotseatIcons = srcHotseatItems.size
|
val numSrcDatabaseHotseatIcons = srcHotseatItems.size
|
||||||
idp.numDatabaseHotseatIcons = 6
|
idp.numDatabaseHotseatIcons = 6
|
||||||
@@ -532,9 +549,9 @@ class GridSizeMigrationUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
fun migrateFromLargerHotseat() {
|
fun migrateFromLargerHotseat() {
|
||||||
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
|
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
|
||||||
addItem(ITEM_TYPE_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
|
addItem(ITEM_TYPE_DEEP_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
|
||||||
addItem(ITEM_TYPE_APPLICATION, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
|
addItem(ITEM_TYPE_APPLICATION, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
|
||||||
addItem(ITEM_TYPE_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
|
addItem(ITEM_TYPE_DEEP_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
|
||||||
addItem(ITEM_TYPE_APPLICATION, 5, CONTAINER_HOTSEAT, 0, 0, testPackage5, 5, TMP_TABLE)
|
addItem(ITEM_TYPE_APPLICATION, 5, CONTAINER_HOTSEAT, 0, 0, testPackage5, 5, TMP_TABLE)
|
||||||
|
|
||||||
idp.numDatabaseHotseatIcons = 4
|
idp.numDatabaseHotseatIcons = 4
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.ICON;
|
|||||||
import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
|
import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.OPTIONS;
|
import static com.android.launcher3.LauncherSettings.Favorites.OPTIONS;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID;
|
import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.RANK;
|
import static com.android.launcher3.LauncherSettings.Favorites.RANK;
|
||||||
@@ -158,13 +158,13 @@ public class LoaderCursorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loadSimpleShortcut() {
|
public void loadSimpleShortcut() {
|
||||||
initCursor(ITEM_TYPE_SHORTCUT, "my-shortcut");
|
initCursor(ITEM_TYPE_DEEP_SHORTCUT, "my-shortcut");
|
||||||
assertTrue(mLoaderCursor.moveToNext());
|
assertTrue(mLoaderCursor.moveToNext());
|
||||||
|
|
||||||
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
|
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
|
||||||
assertTrue(mApp.getIconCache().isDefaultIcon(info.bitmap, info.user));
|
assertTrue(mApp.getIconCache().isDefaultIcon(info.bitmap, info.user));
|
||||||
assertEquals("my-shortcut", info.title);
|
assertEquals("my-shortcut", info.title);
|
||||||
assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
|
assertEquals(ITEM_TYPE_DEEP_SHORTCUT, info.itemType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
package com.android.launcher3.model;
|
package com.android.launcher3.model;
|
||||||
|
|
||||||
|
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3;
|
||||||
|
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
|
||||||
|
import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
@@ -9,6 +16,8 @@ import com.android.launcher3.model.data.ItemInfo;
|
|||||||
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
|
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
|
||||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||||
import com.android.launcher3.pm.PackageInstallInfo;
|
import com.android.launcher3.pm.PackageInstallInfo;
|
||||||
|
import com.android.launcher3.util.IntSet;
|
||||||
|
import com.android.launcher3.util.LauncherLayoutBuilder;
|
||||||
import com.android.launcher3.util.LauncherModelHelper;
|
import com.android.launcher3.util.LauncherModelHelper;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@@ -16,9 +25,6 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link PackageInstallStateChangedTask}
|
* Tests for {@link PackageInstallStateChangedTask}
|
||||||
*/
|
*/
|
||||||
@@ -26,12 +32,36 @@ import java.util.HashSet;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class PackageInstallStateChangedTaskTest {
|
public class PackageInstallStateChangedTaskTest {
|
||||||
|
|
||||||
|
private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
|
||||||
|
private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
|
||||||
|
|
||||||
private LauncherModelHelper mModelHelper;
|
private LauncherModelHelper mModelHelper;
|
||||||
|
private IntSet mDownloadingApps;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
mModelHelper = new LauncherModelHelper();
|
mModelHelper = new LauncherModelHelper();
|
||||||
mModelHelper.initializeData("package_install_state_change_task_data");
|
mModelHelper.createInstallerSession(PENDING_APP_1);
|
||||||
|
mModelHelper.createInstallerSession(PENDING_APP_2);
|
||||||
|
|
||||||
|
LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
|
||||||
|
.atWorkspace(0, 0, 1).putApp(TEST_PACKAGE, TEST_ACTIVITY) // 1
|
||||||
|
.atWorkspace(0, 0, 2).putApp(TEST_PACKAGE, TEST_ACTIVITY2) // 2
|
||||||
|
.atWorkspace(0, 0, 3).putApp(TEST_PACKAGE, TEST_ACTIVITY3) // 3
|
||||||
|
|
||||||
|
.atWorkspace(0, 0, 4).putApp(PENDING_APP_1, TEST_ACTIVITY) // 4
|
||||||
|
.atWorkspace(0, 0, 5).putApp(PENDING_APP_1, TEST_ACTIVITY2) // 5
|
||||||
|
.atWorkspace(0, 0, 6).putApp(PENDING_APP_1, TEST_ACTIVITY3) // 6
|
||||||
|
.atWorkspace(0, 0, 7).putWidget(PENDING_APP_1, "pending.widget", 1, 1) // 7
|
||||||
|
|
||||||
|
.atWorkspace(0, 0, 8).putApp(PENDING_APP_2, TEST_ACTIVITY) // 8
|
||||||
|
.atWorkspace(0, 0, 9).putApp(PENDING_APP_2, TEST_ACTIVITY2) // 9
|
||||||
|
.atWorkspace(0, 0, 10).putApp(PENDING_APP_2, TEST_ACTIVITY3); // 10
|
||||||
|
|
||||||
|
mDownloadingApps = IntSet.wrap(4, 5, 6, 7, 8, 9, 10);
|
||||||
|
mModelHelper.setupDefaultLayoutProvider(builder);
|
||||||
|
mModelHelper.loadModelSync();
|
||||||
|
assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -47,36 +77,45 @@ public class PackageInstallStateChangedTaskTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionUpdate_ignore_installed() throws Exception {
|
public void testSessionUpdate_ignore_installed() {
|
||||||
mModelHelper.executeTaskForTest(newTask("app1", 30));
|
// Run on model executor so that no other task runs in the middle.
|
||||||
|
runOnExecutorSync(MODEL_EXECUTOR, () -> {
|
||||||
|
mModelHelper.getModel().enqueueModelUpdateTask(newTask(TEST_PACKAGE, 30));
|
||||||
|
|
||||||
// No shortcuts were updated
|
// No shortcuts were updated
|
||||||
verifyProgressUpdate(0);
|
verifyProgressUpdate(0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionUpdate_shortcuts_updated() throws Exception {
|
public void testSessionUpdate_shortcuts_updated() {
|
||||||
mModelHelper.executeTaskForTest(newTask("app3", 30));
|
// Run on model executor so that no other task runs in the middle.
|
||||||
|
runOnExecutorSync(MODEL_EXECUTOR, () -> {
|
||||||
|
mModelHelper.getModel().enqueueModelUpdateTask(newTask(PENDING_APP_1, 30));
|
||||||
|
|
||||||
verifyProgressUpdate(30, 5, 6, 7);
|
verifyProgressUpdate(30, 4, 5, 6, 7);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionUpdate_widgets_updated() throws Exception {
|
public void testSessionUpdate_widgets_updated() {
|
||||||
mModelHelper.executeTaskForTest(newTask("app4", 30));
|
// Run on model executor so that no other task runs in the middle.
|
||||||
|
runOnExecutorSync(MODEL_EXECUTOR, () -> {
|
||||||
|
mModelHelper.getModel().enqueueModelUpdateTask(newTask(PENDING_APP_2, 30));
|
||||||
|
|
||||||
verifyProgressUpdate(30, 8, 9);
|
verifyProgressUpdate(30, 8, 9, 10);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyProgressUpdate(int progress, Integer... idsUpdated) {
|
private void verifyProgressUpdate(int progress, int... idsUpdated) {
|
||||||
HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
|
IntSet updates = IntSet.wrap(idsUpdated);
|
||||||
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
|
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
|
||||||
if (info instanceof WorkspaceItemInfo) {
|
int expectedProgress = updates.contains(info.id) ? progress
|
||||||
assertEquals(updates.contains(info.id) ? progress: 100,
|
: (mDownloadingApps.contains(info.id) ? 0 : 100);
|
||||||
((WorkspaceItemInfo) info).getProgressLevel());
|
if (info instanceof WorkspaceItemInfo wi) {
|
||||||
|
assertEquals(expectedProgress, wi.getProgressLevel());
|
||||||
} else {
|
} else {
|
||||||
assertEquals(updates.contains(info.id) ? progress: -1,
|
assertEquals(expectedProgress, ((LauncherAppWidgetInfo) info).installProgress);
|
||||||
((LauncherAppWidgetInfo) info).installProgress);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,9 +87,8 @@ class DisplayControllerTest {
|
|||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
whenever(context.getObject(eq(WindowManagerProxy.INSTANCE), any()))
|
whenever(context.getObject(eq(WindowManagerProxy.INSTANCE))).thenReturn(windowManagerProxy)
|
||||||
.thenReturn(windowManagerProxy)
|
whenever(context.getObject(eq(LauncherPrefs.INSTANCE))).thenReturn(launcherPrefs)
|
||||||
whenever(context.getObject(eq(LauncherPrefs.INSTANCE), any())).thenReturn(launcherPrefs)
|
|
||||||
|
|
||||||
// Mock WindowManagerProxy
|
// Mock WindowManagerProxy
|
||||||
val displayInfo =
|
val displayInfo =
|
||||||
|
|||||||
@@ -15,34 +15,31 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.launcher3.util;
|
package com.android.launcher3.util;
|
||||||
|
|
||||||
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL;
|
||||||
|
|
||||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
|
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTENT_URI;
|
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||||
|
import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.atLeast;
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.pm.PackageInstaller;
|
||||||
import android.content.Context;
|
import android.content.pm.PackageInstaller.SessionParams;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ProviderInfo;
|
import android.content.pm.ProviderInfo;
|
||||||
import android.content.res.Resources;
|
import android.graphics.Bitmap;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.graphics.Bitmap.Config;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
|
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
|
||||||
import android.os.Process;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.test.mock.MockContentResolver;
|
import android.test.mock.MockContentResolver;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
@@ -57,13 +54,10 @@ import com.android.launcher3.LauncherModel;
|
|||||||
import com.android.launcher3.LauncherModel.ModelUpdateTask;
|
import com.android.launcher3.LauncherModel.ModelUpdateTask;
|
||||||
import com.android.launcher3.LauncherPrefs;
|
import com.android.launcher3.LauncherPrefs;
|
||||||
import com.android.launcher3.LauncherProvider;
|
import com.android.launcher3.LauncherProvider;
|
||||||
import com.android.launcher3.LauncherSettings;
|
|
||||||
import com.android.launcher3.model.AllAppsList;
|
import com.android.launcher3.model.AllAppsList;
|
||||||
import com.android.launcher3.model.BgDataModel;
|
import com.android.launcher3.model.BgDataModel;
|
||||||
import com.android.launcher3.model.BgDataModel.Callbacks;
|
import com.android.launcher3.model.BgDataModel.Callbacks;
|
||||||
import com.android.launcher3.model.ItemInstallQueue;
|
import com.android.launcher3.model.ItemInstallQueue;
|
||||||
import com.android.launcher3.model.data.AppInfo;
|
|
||||||
import com.android.launcher3.model.data.ItemInfo;
|
|
||||||
import com.android.launcher3.pm.InstallSessionHelper;
|
import com.android.launcher3.pm.InstallSessionHelper;
|
||||||
import com.android.launcher3.pm.UserCache;
|
import com.android.launcher3.pm.UserCache;
|
||||||
import com.android.launcher3.testing.TestInformationProvider;
|
import com.android.launcher3.testing.TestInformationProvider;
|
||||||
@@ -72,37 +66,25 @@ import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
|
|||||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||||
import com.android.launcher3.widget.custom.CustomWidgetManager;
|
import com.android.launcher3.widget.custom.CustomWidgetManager;
|
||||||
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to help manage Launcher Model and related objects for test.
|
* Utility class to help manage Launcher Model and related objects for test.
|
||||||
*/
|
*/
|
||||||
public class LauncherModelHelper {
|
public class LauncherModelHelper {
|
||||||
|
|
||||||
public static final int DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
public static final String TEST_PACKAGE = getInstrumentation().getContext().getPackageName();
|
||||||
public static final int HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
|
||||||
|
|
||||||
public static final int APP_ICON = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
|
||||||
public static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
|
||||||
public static final int NO__ICON = -1;
|
|
||||||
|
|
||||||
public static final String TEST_PACKAGE = testContext().getPackageName();
|
|
||||||
public static final String TEST_ACTIVITY = "com.android.launcher3.tests.Activity2";
|
public static final String TEST_ACTIVITY = "com.android.launcher3.tests.Activity2";
|
||||||
|
public static final String TEST_ACTIVITY2 = "com.android.launcher3.tests.Activity3";
|
||||||
|
public static final String TEST_ACTIVITY3 = "com.android.launcher3.tests.Activity4";
|
||||||
|
|
||||||
// Authority for providing a test default-workspace-layout data.
|
// Authority for providing a test default-workspace-layout data.
|
||||||
private static final String TEST_PROVIDER_AUTHORITY =
|
private static final String TEST_PROVIDER_AUTHORITY =
|
||||||
@@ -110,39 +92,18 @@ public class LauncherModelHelper {
|
|||||||
private static final int DEFAULT_BITMAP_SIZE = 10;
|
private static final int DEFAULT_BITMAP_SIZE = 10;
|
||||||
private static final int DEFAULT_GRID_SIZE = 4;
|
private static final int DEFAULT_GRID_SIZE = 4;
|
||||||
|
|
||||||
private final HashMap<Class, HashMap<String, Field>> mFieldCache = new HashMap<>();
|
|
||||||
private final MockContentResolver mMockResolver = new MockContentResolver();
|
|
||||||
public final TestLauncherProvider provider;
|
|
||||||
public final SandboxModelContext sandboxContext;
|
public final SandboxModelContext sandboxContext;
|
||||||
|
|
||||||
public final long defaultProfileId;
|
private final RunnableList mDestroyTask = new RunnableList();
|
||||||
|
|
||||||
private BgDataModel mDataModel;
|
private BgDataModel mDataModel;
|
||||||
private AllAppsList mAllAppsList;
|
|
||||||
|
|
||||||
public LauncherModelHelper() {
|
public LauncherModelHelper() {
|
||||||
Context context = getApplicationContext();
|
|
||||||
// System settings cache content provider. Ensure that they are statically initialized
|
|
||||||
Settings.Secure.getString(context.getContentResolver(), "test");
|
|
||||||
Settings.System.getString(context.getContentResolver(), "test");
|
|
||||||
Settings.Global.getString(context.getContentResolver(), "test");
|
|
||||||
|
|
||||||
provider = new TestLauncherProvider();
|
|
||||||
sandboxContext = new SandboxModelContext();
|
sandboxContext = new SandboxModelContext();
|
||||||
defaultProfileId = UserCache.INSTANCE.get(sandboxContext)
|
|
||||||
.getSerialNumberForUser(Process.myUserHandle());
|
|
||||||
setupProvider(LauncherProvider.AUTHORITY, provider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupProvider(String authority, ContentProvider provider) {
|
public void setupProvider(String authority, ContentProvider provider) {
|
||||||
ProviderInfo providerInfo = new ProviderInfo();
|
sandboxContext.setupProvider(authority, provider);
|
||||||
providerInfo.authority = authority;
|
|
||||||
providerInfo.applicationInfo = sandboxContext.getApplicationInfo();
|
|
||||||
provider.attachInfo(sandboxContext, providerInfo);
|
|
||||||
mMockResolver.addProvider(providerInfo.authority, provider);
|
|
||||||
doReturn(providerInfo)
|
|
||||||
.when(sandboxContext.mPm)
|
|
||||||
.resolveContentProvider(eq(authority), anyInt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LauncherModel getModel() {
|
public LauncherModel getModel() {
|
||||||
@@ -151,16 +112,35 @@ public class LauncherModelHelper {
|
|||||||
|
|
||||||
public synchronized BgDataModel getBgDataModel() {
|
public synchronized BgDataModel getBgDataModel() {
|
||||||
if (mDataModel == null) {
|
if (mDataModel == null) {
|
||||||
mDataModel = ReflectionHelpers.getField(getModel(), "mBgDataModel");
|
getModel().enqueueModelUpdateTask(new ModelUpdateTask() {
|
||||||
|
@Override
|
||||||
|
public void init(@NonNull LauncherAppState app, @NonNull LauncherModel model,
|
||||||
|
@NonNull BgDataModel dataModel, @NonNull AllAppsList allAppsList,
|
||||||
|
@NonNull Executor uiExecutor) {
|
||||||
|
mDataModel = dataModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() { }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return mDataModel;
|
return mDataModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized AllAppsList getAllAppsList() {
|
/**
|
||||||
if (mAllAppsList == null) {
|
* Creates a installer session for the provided package.
|
||||||
mAllAppsList = ReflectionHelpers.getField(getModel(), "mBgAllAppsList");
|
*/
|
||||||
}
|
public int createInstallerSession(String pkg) throws IOException {
|
||||||
return mAllAppsList;
|
SessionParams sp = new SessionParams(MODE_FULL_INSTALL);
|
||||||
|
sp.setAppPackageName(pkg);
|
||||||
|
Bitmap icon = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
|
||||||
|
icon.eraseColor(Color.RED);
|
||||||
|
sp.setAppIcon(icon);
|
||||||
|
sp.setAppLabel(pkg);
|
||||||
|
PackageInstaller pi = sandboxContext.getPackageManager().getPackageInstaller();
|
||||||
|
int sessionId = pi.createSession(sp);
|
||||||
|
mDestroyTask.add(() -> pi.abandonSession(sessionId));
|
||||||
|
return sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
@@ -175,6 +155,8 @@ public class LauncherModelHelper {
|
|||||||
waitOrThrow(l1);
|
waitOrThrow(l1);
|
||||||
sandboxContext.onDestroy();
|
sandboxContext.onDestroy();
|
||||||
l2.countDown();
|
l2.countDown();
|
||||||
|
|
||||||
|
mDestroyTask.executeAllAndDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitOrThrow(CountDownLatch latch) {
|
private void waitOrThrow(CountDownLatch latch) {
|
||||||
@@ -185,184 +167,6 @@ public class LauncherModelHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronously executes the task and returns all the UI callbacks posted.
|
|
||||||
*/
|
|
||||||
public List<Runnable> executeTaskForTest(ModelUpdateTask task) throws Exception {
|
|
||||||
LauncherModel model = getModel();
|
|
||||||
if (!model.isModelLoaded()) {
|
|
||||||
ReflectionHelpers.setField(model, "mModelLoaded", true);
|
|
||||||
}
|
|
||||||
Executor mockExecutor = mock(Executor.class);
|
|
||||||
model.enqueueModelUpdateTask(new ModelUpdateTask() {
|
|
||||||
@Override
|
|
||||||
public void init(@NonNull final LauncherAppState app,
|
|
||||||
@NonNull final LauncherModel model, @NonNull final BgDataModel dataModel,
|
|
||||||
@NonNull final AllAppsList allAppsList, @NonNull final Executor uiExecutor) {
|
|
||||||
task.init(app, model, dataModel, allAppsList, mockExecutor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
task.run();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
MODEL_EXECUTOR.submit(() -> null).get();
|
|
||||||
|
|
||||||
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
|
|
||||||
verify(mockExecutor, atLeast(0)).execute(captor.capture());
|
|
||||||
return captor.getAllValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronously executes a task on the model
|
|
||||||
*/
|
|
||||||
public <T> T executeSimpleTask(Function<BgDataModel, T> task) throws Exception {
|
|
||||||
BgDataModel dataModel = getBgDataModel();
|
|
||||||
return MODEL_EXECUTOR.submit(() -> task.apply(dataModel)).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes mock data for the test.
|
|
||||||
*/
|
|
||||||
public void initializeData(String resourceName) throws Exception {
|
|
||||||
BgDataModel bgDataModel = getBgDataModel();
|
|
||||||
AllAppsList allAppsList = getAllAppsList();
|
|
||||||
|
|
||||||
MODEL_EXECUTOR.submit(() -> {
|
|
||||||
// Copy apk from resources to a local file and install from there.
|
|
||||||
Resources resources = testContext().getResources();
|
|
||||||
int resId = resources.getIdentifier(
|
|
||||||
resourceName, "raw", testContext().getPackageName());
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
|
|
||||||
resources.openRawResource(resId)))) {
|
|
||||||
String line;
|
|
||||||
HashMap<String, Class> classMap = new HashMap<>();
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
line = line.trim();
|
|
||||||
if (line.startsWith("#") || line.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String[] commands = line.split(" ");
|
|
||||||
switch (commands[0]) {
|
|
||||||
case "classMap":
|
|
||||||
classMap.put(commands[1], Class.forName(commands[2]));
|
|
||||||
break;
|
|
||||||
case "bgItem":
|
|
||||||
bgDataModel.addItem(sandboxContext,
|
|
||||||
(ItemInfo) initItem(classMap.get(commands[1]), commands, 2),
|
|
||||||
false);
|
|
||||||
break;
|
|
||||||
case "allApps":
|
|
||||||
allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object initItem(Class clazz, String[] fieldDef, int startIndex) throws Exception {
|
|
||||||
HashMap<String, Field> cache = mFieldCache.get(clazz);
|
|
||||||
if (cache == null) {
|
|
||||||
cache = new HashMap<>();
|
|
||||||
Class c = clazz;
|
|
||||||
while (c != null) {
|
|
||||||
for (Field f : c.getDeclaredFields()) {
|
|
||||||
f.setAccessible(true);
|
|
||||||
cache.put(f.getName(), f);
|
|
||||||
}
|
|
||||||
c = c.getSuperclass();
|
|
||||||
}
|
|
||||||
mFieldCache.put(clazz, cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object item = clazz.newInstance();
|
|
||||||
for (int i = startIndex; i < fieldDef.length; i++) {
|
|
||||||
String[] fieldData = fieldDef[i].split("=", 2);
|
|
||||||
Field f = cache.get(fieldData[0]);
|
|
||||||
Class type = f.getType();
|
|
||||||
if (type == int.class || type == long.class) {
|
|
||||||
f.set(item, Integer.parseInt(fieldData[1]));
|
|
||||||
} else if (type == CharSequence.class || type == String.class) {
|
|
||||||
f.set(item, fieldData[1]);
|
|
||||||
} else if (type == Intent.class) {
|
|
||||||
if (!fieldData[1].startsWith("#Intent")) {
|
|
||||||
fieldData[1] = "#Intent;" + fieldData[1] + ";end";
|
|
||||||
}
|
|
||||||
f.set(item, Intent.parseUri(fieldData[1], 0));
|
|
||||||
} else if (type == ComponentName.class) {
|
|
||||||
f.set(item, ComponentName.unflattenFromString(fieldData[1]));
|
|
||||||
} else {
|
|
||||||
throw new Exception("Added parsing logic for "
|
|
||||||
+ f.getName() + " of type " + f.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int addItem(int type, int screen, int container, int x, int y) {
|
|
||||||
return addItem(type, screen, container, x, y, defaultProfileId, TEST_PACKAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int addItem(int type, int screen, int container, int x, int y, long profileId) {
|
|
||||||
return addItem(type, screen, container, x, y, profileId, TEST_PACKAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int addItem(int type, int screen, int container, int x, int y, String packageName) {
|
|
||||||
return addItem(type, screen, container, x, y, defaultProfileId, packageName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int addItem(int type, int screen, int container, int x, int y, String packageName,
|
|
||||||
int id, Uri contentUri) {
|
|
||||||
addItem(type, screen, container, x, y, defaultProfileId, packageName, id, contentUri);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a mock item in the DB.
|
|
||||||
* @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
|
|
||||||
* folder (where the type represents the number of items in the folder).
|
|
||||||
*/
|
|
||||||
public int addItem(int type, int screen, int container, int x, int y, long profileId,
|
|
||||||
String packageName) {
|
|
||||||
int id = LauncherSettings.Settings.call(sandboxContext.getContentResolver(),
|
|
||||||
LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
|
|
||||||
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
|
|
||||||
addItem(type, screen, container, x, y, profileId, packageName, id, CONTENT_URI);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addItem(int type, int screen, int container, int x, int y, long profileId,
|
|
||||||
String packageName, int id, Uri contentUri) {
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(LauncherSettings.Favorites._ID, id);
|
|
||||||
values.put(LauncherSettings.Favorites.CONTAINER, container);
|
|
||||||
values.put(LauncherSettings.Favorites.SCREEN, screen);
|
|
||||||
values.put(LauncherSettings.Favorites.CELLX, x);
|
|
||||||
values.put(LauncherSettings.Favorites.CELLY, y);
|
|
||||||
values.put(LauncherSettings.Favorites.SPANX, 1);
|
|
||||||
values.put(LauncherSettings.Favorites.SPANY, 1);
|
|
||||||
values.put(LauncherSettings.Favorites.PROFILE_ID, profileId);
|
|
||||||
|
|
||||||
if (type == APP_ICON || type == SHORTCUT) {
|
|
||||||
values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
|
|
||||||
values.put(LauncherSettings.Favorites.INTENT,
|
|
||||||
new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0));
|
|
||||||
} else {
|
|
||||||
values.put(LauncherSettings.Favorites.ITEM_TYPE,
|
|
||||||
LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
|
|
||||||
// Add folder items.
|
|
||||||
for (int i = 0; i < type; i++) {
|
|
||||||
addItem(APP_ICON, 0, id, 0, 0, profileId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sandboxContext.getContentResolver().insert(contentUri, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up a mock provider to load the provided layout by default, next time the layout loads
|
* Sets up a mock provider to load the provided layout by default, next time the layout loads
|
||||||
*/
|
*/
|
||||||
@@ -394,6 +198,9 @@ public class LauncherModelHelper {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
setupProvider(TEST_PROVIDER_AUTHORITY, cp);
|
setupProvider(TEST_PROVIDER_AUTHORITY, cp);
|
||||||
|
mDestroyTask.add(() -> runOnExecutorSync(MODEL_EXECUTOR, () ->
|
||||||
|
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
|
||||||
|
"settings delete secure launcher3.layout.provider")));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,46 +209,16 @@ public class LauncherModelHelper {
|
|||||||
*/
|
*/
|
||||||
public void loadModelSync() throws ExecutionException, InterruptedException {
|
public void loadModelSync() throws ExecutionException, InterruptedException {
|
||||||
Callbacks mockCb = new Callbacks() { };
|
Callbacks mockCb = new Callbacks() { };
|
||||||
Executors.MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get();
|
MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get();
|
||||||
|
|
||||||
Executors.MODEL_EXECUTOR.submit(() -> { }).get();
|
Executors.MODEL_EXECUTOR.submit(() -> { }).get();
|
||||||
Executors.MAIN_EXECUTOR.submit(() -> { }).get();
|
MAIN_EXECUTOR.submit(() -> { }).get();
|
||||||
Executors.MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get();
|
MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class SandboxModelContext extends SandboxContext {
|
||||||
* An extension of LauncherProvider backed up by in-memory database.
|
|
||||||
*/
|
|
||||||
public static class TestLauncherProvider extends LauncherProvider {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreate() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLiteDatabase getDb() {
|
|
||||||
return getModelDbController().getDb();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean deleteContents(File dir) {
|
|
||||||
File[] files = dir.listFiles();
|
|
||||||
boolean success = true;
|
|
||||||
if (files != null) {
|
|
||||||
for (File file : files) {
|
|
||||||
if (file.isDirectory()) {
|
|
||||||
success &= deleteContents(file);
|
|
||||||
}
|
|
||||||
if (!file.delete()) {
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SandboxModelContext extends SandboxContext {
|
|
||||||
|
|
||||||
|
private final MockContentResolver mMockResolver = new MockContentResolver();
|
||||||
private final ArrayMap<String, Object> mSpiedServices = new ArrayMap<>();
|
private final ArrayMap<String, Object> mSpiedServices = new ArrayMap<>();
|
||||||
private final PackageManager mPm;
|
private final PackageManager mPm;
|
||||||
private final File mDbDir;
|
private final File mDbDir;
|
||||||
@@ -453,8 +230,31 @@ public class LauncherModelHelper {
|
|||||||
DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
|
DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
|
||||||
SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE, LockedUserState.INSTANCE,
|
SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE, LockedUserState.INSTANCE,
|
||||||
ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE);
|
ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE);
|
||||||
|
|
||||||
|
// System settings cache content provider. Ensure that they are statically initialized
|
||||||
|
Settings.Secure.getString(
|
||||||
|
ApplicationProvider.getApplicationContext().getContentResolver(), "test");
|
||||||
|
Settings.System.getString(
|
||||||
|
ApplicationProvider.getApplicationContext().getContentResolver(), "test");
|
||||||
|
Settings.Global.getString(
|
||||||
|
ApplicationProvider.getApplicationContext().getContentResolver(), "test");
|
||||||
|
|
||||||
mPm = spy(getBaseContext().getPackageManager());
|
mPm = spy(getBaseContext().getPackageManager());
|
||||||
mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
|
mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
|
||||||
|
setupProvider(LauncherProvider.AUTHORITY, new LauncherProvider() {
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected <T> T createObject(MainThreadInitializedObject<T> object) {
|
||||||
|
if (object == LauncherAppState.INSTANCE) {
|
||||||
|
return (T) new LauncherAppState(this, null /* iconCacheFileName */);
|
||||||
|
}
|
||||||
|
return super.createObject(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SandboxModelContext allow(MainThreadInitializedObject object) {
|
public SandboxModelContext allow(MainThreadInitializedObject object) {
|
||||||
@@ -505,9 +305,30 @@ public class LauncherModelHelper {
|
|||||||
mSpiedServices.put(name, result);
|
mSpiedServices.put(name, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static Context testContext() {
|
public void setupProvider(String authority, ContentProvider provider) {
|
||||||
return getInstrumentation().getContext();
|
ProviderInfo providerInfo = new ProviderInfo();
|
||||||
|
providerInfo.authority = authority;
|
||||||
|
providerInfo.applicationInfo = getApplicationInfo();
|
||||||
|
provider.attachInfo(this, providerInfo);
|
||||||
|
mMockResolver.addProvider(providerInfo.authority, provider);
|
||||||
|
doReturn(providerInfo).when(mPm).resolveContentProvider(eq(authority), anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean deleteContents(File dir) {
|
||||||
|
File[] files = dir.listFiles();
|
||||||
|
boolean success = true;
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
success &= deleteContents(file);
|
||||||
|
}
|
||||||
|
if (!file.delete()) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.ToIntFunction;
|
import java.util.function.ToIntFunction;
|
||||||
|
|
||||||
@@ -154,6 +155,30 @@ public class TestUtil {
|
|||||||
Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, null);
|
Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method to run a task synchronously which converts any exceptions to RuntimeException
|
||||||
|
*/
|
||||||
|
public static void runOnExecutorSync(ExecutorService executor, UncheckedRunnable task) {
|
||||||
|
try {
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
task.run();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Interface to indicate a runnable which can throw any exception. */
|
||||||
|
public interface UncheckedRunnable {
|
||||||
|
/** Method to run the task */
|
||||||
|
void run() throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class PackageInstallCheck extends LauncherApps.Callback
|
private static class PackageInstallCheck extends LauncherApps.Callback
|
||||||
implements AutoCloseable {
|
implements AutoCloseable {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user