Merge "Moving some tests to robolectric" into ub-launcher3-master
This commit is contained in:
committed by
Android (Google) Code Review
commit
ebc50f3095
@@ -27,7 +27,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
mockito-robolectric-prebuilt \
|
||||
truth-prebuilt
|
||||
LOCAL_JAVA_LIBRARIES := \
|
||||
platform-robolectric-3.6.1-prebuilt
|
||||
platform-robolectric-4.3-prebuilt
|
||||
|
||||
LOCAL_JAVA_RESOURCE_DIRS := resources config
|
||||
|
||||
@@ -54,4 +54,4 @@ LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src \
|
||||
|
||||
LOCAL_ROBOTEST_TIMEOUT := 36000
|
||||
|
||||
include prebuilts/misc/common/robolectric/3.6.1/run_robotests.mk
|
||||
include prebuilts/misc/common/robolectric/4.3/run_robotests.mk
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.android.launcher3.model;
|
||||
|
||||
import static com.android.launcher3.shadows.ShadowLooperExecutor.reinitializeStaticExecutors;
|
||||
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Mockito.atLeast;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.util.ReflectionHelpers.setField;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -29,6 +32,7 @@ import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.icons.cache.CachingLogic;
|
||||
import com.android.launcher3.model.BgDataModel.Callbacks;
|
||||
import com.android.launcher3.pm.PackageInstallerCompat;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.TestLauncherProvider;
|
||||
|
||||
@@ -53,7 +57,7 @@ import java.util.function.Supplier;
|
||||
public class BaseModelUpdateTaskTestCase {
|
||||
|
||||
public final HashMap<Class, HashMap<String, Field>> fieldCache = new HashMap<>();
|
||||
private TestLauncherProvider mProvider;
|
||||
public TestLauncherProvider provider;
|
||||
|
||||
public Context targetContext;
|
||||
public UserHandle myUser;
|
||||
@@ -71,9 +75,11 @@ public class BaseModelUpdateTaskTestCase {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
ShadowLog.stream = System.out;
|
||||
reinitializeStaticExecutors();
|
||||
setField(PackageInstallerCompat.class, null, "sInstance", null);
|
||||
|
||||
mProvider = Robolectric.setupContentProvider(TestLauncherProvider.class);
|
||||
ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, mProvider);
|
||||
provider = Robolectric.setupContentProvider(TestLauncherProvider.class);
|
||||
ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, provider);
|
||||
|
||||
callbacks = mock(Callbacks.class);
|
||||
appState = mock(LauncherAppState.class);
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.model;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
import static org.robolectric.util.ReflectionHelpers.setField;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.SessionInfo;
|
||||
import android.content.pm.PackageInstaller.SessionParams;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.launcher3.FolderInfo;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.LauncherProvider;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.shadows.LShadowLauncherApps;
|
||||
import com.android.launcher3.shadows.LShadowUserManager;
|
||||
import com.android.launcher3.shadows.ShadowLooperExecutor;
|
||||
import com.android.launcher3.util.Executors;
|
||||
import com.android.launcher3.util.LauncherLayoutBuilder;
|
||||
import com.android.launcher3.widget.custom.CustomWidgetManager;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
import org.robolectric.annotation.LooperMode.Mode;
|
||||
import org.robolectric.shadows.ShadowPackageManager;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Tests for layout parser for remote layout
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {LShadowUserManager.class, LShadowLauncherApps.class, ShadowLooperExecutor.class})
|
||||
@LooperMode(Mode.PAUSED)
|
||||
public class DefaultLayoutProviderTest extends BaseModelUpdateTaskTestCase {
|
||||
|
||||
private static final String SETTINGS_APP = "com.android.settings";
|
||||
private static final String TEST_PROVIDER_AUTHORITY =
|
||||
DefaultLayoutProviderTest.class.getName().toLowerCase();
|
||||
|
||||
private static final int BITMAP_SIZE = 10;
|
||||
private static final int GRID_SIZE = 4;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
InvariantDeviceProfile.INSTANCE.initializeForTesting(idp);
|
||||
CustomWidgetManager.INSTANCE.initializeForTesting(mock(CustomWidgetManager.class));
|
||||
|
||||
idp.numRows = idp.numColumns = idp.numHotseatIcons = GRID_SIZE;
|
||||
idp.iconBitmapSize = BITMAP_SIZE;
|
||||
|
||||
provider.setAllowLoadDefaultFavorites(true);
|
||||
Settings.Secure.putString(targetContext.getContentResolver(),
|
||||
"launcher3.layout.provider", TEST_PROVIDER_AUTHORITY);
|
||||
|
||||
ShadowPackageManager spm = shadowOf(targetContext.getPackageManager());
|
||||
spm.addProviderIfNotPresent(new ComponentName("com.test", "Dummy")).authority =
|
||||
TEST_PROVIDER_AUTHORITY;
|
||||
spm.addActivityIfNotPresent(new ComponentName(SETTINGS_APP, SETTINGS_APP));
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
InvariantDeviceProfile.INSTANCE.initializeForTesting(null);
|
||||
CustomWidgetManager.INSTANCE.initializeForTesting(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
|
||||
writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0)
|
||||
.putApp(SETTINGS_APP, SETTINGS_APP));
|
||||
|
||||
// Verify one item in hotseat
|
||||
assertEquals(1, bgDataModel.workspaceItems.size());
|
||||
ItemInfo info = bgDataModel.workspaceItems.get(0);
|
||||
assertEquals(LauncherSettings.Favorites.CONTAINER_HOTSEAT, info.container);
|
||||
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPLICATION, info.itemType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomProfileLoaded_with_folder() throws Exception {
|
||||
writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
|
||||
.addApp(SETTINGS_APP, SETTINGS_APP)
|
||||
.addApp(SETTINGS_APP, SETTINGS_APP)
|
||||
.addApp(SETTINGS_APP, SETTINGS_APP)
|
||||
.build());
|
||||
|
||||
// Verify folder
|
||||
assertEquals(1, bgDataModel.workspaceItems.size());
|
||||
ItemInfo info = bgDataModel.workspaceItems.get(0);
|
||||
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
|
||||
assertEquals(3, ((FolderInfo) info).contents.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomProfileLoaded_with_widget() throws Exception {
|
||||
String pendingAppPkg = "com.test.pending";
|
||||
|
||||
// Add a dummy session info so that the widget exists
|
||||
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
|
||||
params.setAppPackageName(pendingAppPkg);
|
||||
|
||||
PackageInstaller installer = targetContext.getPackageManager().getPackageInstaller();
|
||||
int sessionId = installer.createSession(params);
|
||||
SessionInfo sessionInfo = installer.getSessionInfo(sessionId);
|
||||
setField(sessionInfo, "installerPackageName", "com.test");
|
||||
setField(sessionInfo, "appIcon", BitmapInfo.LOW_RES_ICON);
|
||||
|
||||
writeLayoutAndLoad(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
|
||||
.putWidget(pendingAppPkg, "DummyWidget", 2, 2));
|
||||
|
||||
// Verify widget
|
||||
assertEquals(1, bgDataModel.appWidgets.size());
|
||||
ItemInfo info = bgDataModel.appWidgets.get(0);
|
||||
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET, info.itemType);
|
||||
assertEquals(2, info.spanX);
|
||||
assertEquals(2, info.spanY);
|
||||
}
|
||||
|
||||
private void writeLayoutAndLoad(LauncherLayoutBuilder builder) throws Exception {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
builder.build(new OutputStreamWriter(bos));
|
||||
|
||||
Uri layoutUri = LauncherProvider.getLayoutUri(TEST_PROVIDER_AUTHORITY, targetContext);
|
||||
shadowOf(targetContext.getContentResolver()).registerInputStream(layoutUri,
|
||||
new ByteArrayInputStream(bos.toByteArray()));
|
||||
|
||||
LoaderResults results = new LoaderResults(appState, bgDataModel, allAppsList, 0,
|
||||
new WeakReference<>(callbacks));
|
||||
LoaderTask task = new LoaderTask(appState, allAppsList, bgDataModel, results);
|
||||
Executors.MODEL_EXECUTOR.submit(() -> task.loadWorkspace(new ArrayList<>())).get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.shadows;
|
||||
|
||||
import static org.robolectric.util.ReflectionHelpers.ClassParameter;
|
||||
import static org.robolectric.util.ReflectionHelpers.callConstructor;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.LauncherActivityInfo;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.shadows.ShadowLauncherApps;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extension of {@link ShadowLauncherApps} with missing shadow methods
|
||||
*/
|
||||
@Implements(value = LauncherApps.class)
|
||||
public class LShadowLauncherApps extends ShadowLauncherApps {
|
||||
|
||||
public final ArraySet<PackageUserKey> disabledApps = new ArraySet<>();
|
||||
public final ArraySet<ComponentKey> disabledActivities = new ArraySet<>();
|
||||
|
||||
@Implementation
|
||||
@Override
|
||||
protected List<ShortcutInfo> getShortcuts(LauncherApps.ShortcutQuery query, UserHandle user) {
|
||||
try {
|
||||
return super.getShortcuts(query, user);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected boolean isPackageEnabled(String packageName, UserHandle user) {
|
||||
return !disabledApps.contains(new PackageUserKey(packageName, user));
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected boolean isActivityEnabled(ComponentName component, UserHandle user) {
|
||||
return !disabledActivities.contains(new ComponentKey(component, user));
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
|
||||
ResolveInfo ri = RuntimeEnvironment.application.getPackageManager()
|
||||
.resolveActivity(intent, 0);
|
||||
return getLauncherActivityInfo(ri.activityInfo);
|
||||
}
|
||||
|
||||
public LauncherActivityInfo getLauncherActivityInfo(ActivityInfo activityInfo) {
|
||||
return callConstructor(LauncherActivityInfo.class,
|
||||
ClassParameter.from(Context.class, RuntimeEnvironment.application),
|
||||
ClassParameter.from(ActivityInfo.class, activityInfo),
|
||||
ClassParameter.from(UserHandle.class, Process.myUserHandle()));
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user)
|
||||
throws PackageManager.NameNotFoundException {
|
||||
return RuntimeEnvironment.application.getPackageManager()
|
||||
.getApplicationInfo(packageName, flags);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.shadows;
|
||||
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.SparseBooleanArray;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.shadows.ShadowUserManager;
|
||||
|
||||
/**
|
||||
* Extension of {@link ShadowUserManager} with missing shadow methods
|
||||
*/
|
||||
@Implements(value = UserManager.class)
|
||||
public class LShadowUserManager extends ShadowUserManager {
|
||||
|
||||
private final SparseBooleanArray mQuietUsers = new SparseBooleanArray();
|
||||
private final SparseBooleanArray mLockedUsers = new SparseBooleanArray();
|
||||
|
||||
@Implementation
|
||||
protected boolean isQuietModeEnabled(UserHandle userHandle) {
|
||||
return mQuietUsers.get(userHandle.hashCode());
|
||||
}
|
||||
|
||||
public void setQuietModeEnabled(UserHandle userHandle, boolean enabled) {
|
||||
mQuietUsers.put(userHandle.hashCode(), enabled);
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected boolean isUserUnlocked(UserHandle userHandle) {
|
||||
return !mLockedUsers.get(userHandle.hashCode());
|
||||
}
|
||||
|
||||
public void setUserLocked(UserHandle userHandle, boolean enabled) {
|
||||
mLockedUsers.put(userHandle.hashCode(), enabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.shadows;
|
||||
|
||||
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
|
||||
|
||||
import static org.robolectric.shadow.api.Shadow.invokeConstructor;
|
||||
import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
|
||||
import static org.robolectric.util.ReflectionHelpers.setField;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.android.launcher3.util.Executors;
|
||||
import com.android.launcher3.util.LooperExecutor;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.annotation.RealObject;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
|
||||
*/
|
||||
@Implements(value = LooperExecutor.class, isInAndroidSdk = false)
|
||||
public class ShadowLooperExecutor {
|
||||
|
||||
// Keep reference to all created Loopers so they can be torn down after test
|
||||
private static Set<LooperExecutor> executors =
|
||||
Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
|
||||
|
||||
@RealObject private LooperExecutor realExecutor;
|
||||
|
||||
@Implementation
|
||||
protected void __constructor__(Looper looper) {
|
||||
invokeConstructor(LooperExecutor.class, realExecutor, from(Looper.class, looper));
|
||||
executors.add(realExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-initializes any executor which may have been reset when a test finished
|
||||
*/
|
||||
public static void reinitializeStaticExecutors() {
|
||||
for (LooperExecutor executor : new ArrayList<>(executors)) {
|
||||
setField(executor, "mHandler",
|
||||
new Handler(createAndStartNewLooper(executor.getThread().getName())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import com.android.launcher3.LauncherProvider;
|
||||
*/
|
||||
public class TestLauncherProvider extends LauncherProvider {
|
||||
|
||||
private boolean mAllowLoadDefaultFavorites;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
@@ -18,18 +20,26 @@ public class TestLauncherProvider extends LauncherProvider {
|
||||
@Override
|
||||
protected synchronized void createDbIfNotExists() {
|
||||
if (mOpenHelper == null) {
|
||||
mOpenHelper = new MyDatabaseHelper(getContext());
|
||||
mOpenHelper = new MyDatabaseHelper(getContext(), mAllowLoadDefaultFavorites);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAllowLoadDefaultFavorites(boolean allowLoadDefaultFavorites) {
|
||||
mAllowLoadDefaultFavorites = allowLoadDefaultFavorites;
|
||||
}
|
||||
|
||||
public SQLiteDatabase getDb() {
|
||||
createDbIfNotExists();
|
||||
return mOpenHelper.getWritableDatabase();
|
||||
}
|
||||
|
||||
private static class MyDatabaseHelper extends DatabaseHelper {
|
||||
public MyDatabaseHelper(Context context) {
|
||||
|
||||
private final boolean mAllowLoadDefaultFavorites;
|
||||
|
||||
MyDatabaseHelper(Context context, boolean allowLoadDefaultFavorites) {
|
||||
super(context, null);
|
||||
mAllowLoadDefaultFavorites = allowLoadDefaultFavorites;
|
||||
initIds();
|
||||
}
|
||||
|
||||
@@ -39,7 +49,11 @@ public class TestLauncherProvider extends LauncherProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEmptyDbCreated() { }
|
||||
protected void onEmptyDbCreated() {
|
||||
if (mAllowLoadDefaultFavorites) {
|
||||
super.onEmptyDbCreated();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
|
||||
|
||||
@@ -484,8 +484,6 @@ public class LauncherProvider extends ContentProvider {
|
||||
*/
|
||||
private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
|
||||
Context ctx = getContext();
|
||||
InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx);
|
||||
|
||||
String authority = Settings.Secure.getString(ctx.getContentResolver(),
|
||||
"launcher3.layout.provider");
|
||||
if (TextUtils.isEmpty(authority)) {
|
||||
@@ -497,13 +495,7 @@ public class LauncherProvider extends ContentProvider {
|
||||
Log.e(TAG, "No provider found for authority " + authority);
|
||||
return null;
|
||||
}
|
||||
Uri uri = new Uri.Builder().scheme("content").authority(authority).path("launcher_layout")
|
||||
.appendQueryParameter("version", "1")
|
||||
.appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
|
||||
.appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
|
||||
.appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
|
||||
.build();
|
||||
|
||||
Uri uri = getLayoutUri(authority, ctx);
|
||||
try (InputStream in = ctx.getContentResolver().openInputStream(uri)) {
|
||||
// Read the full xml so that we fail early in case of any IO error.
|
||||
String layout = new String(IOUtils.toByteArray(in));
|
||||
@@ -520,6 +512,16 @@ public class LauncherProvider extends ContentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public static Uri getLayoutUri(String authority, Context ctx) {
|
||||
InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx);
|
||||
return new Uri.Builder().scheme("content").authority(authority).path("launcher_layout")
|
||||
.appendQueryParameter("version", "1")
|
||||
.appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
|
||||
.appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
|
||||
.appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
|
||||
.build();
|
||||
}
|
||||
|
||||
private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
|
||||
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
|
||||
int defaultLayout = idp.defaultLayoutId;
|
||||
|
||||
@@ -42,6 +42,8 @@ import android.util.LongSparseArray;
|
||||
import android.util.MutableInt;
|
||||
import android.util.TimingLogger;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.AppInfo;
|
||||
import com.android.launcher3.FolderInfo;
|
||||
import com.android.launcher3.InstallShortcutReceiver;
|
||||
@@ -271,7 +273,8 @@ public class LoaderTask implements Runnable {
|
||||
this.notify();
|
||||
}
|
||||
|
||||
private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
|
||||
@VisibleForTesting
|
||||
void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
|
||||
final Context context = mApp.getContext();
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.android.launcher3.ui;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.testcomponent.TestCommandProvider;
|
||||
import com.android.launcher3.util.LauncherLayoutBuilder;
|
||||
import com.android.launcher3.util.rule.ShellCommandRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
@MediumTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
|
||||
|
||||
@Rule
|
||||
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
|
||||
|
||||
private static final String SETTINGS_APP = "com.android.settings";
|
||||
|
||||
private Context mContext;
|
||||
private String mAuthority;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mContext = InstrumentationRegistry.getContext();
|
||||
|
||||
PackageManager pm = mTargetContext.getPackageManager();
|
||||
ProviderInfo pi = pm.getProviderInfo(new ComponentName(mContext,
|
||||
TestCommandProvider.class), 0);
|
||||
mAuthority = pi.authority;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
|
||||
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
|
||||
|
||||
// Launch the home activity
|
||||
mDevice.pressHome();
|
||||
|
||||
mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomProfileLoaded_with_widget() throws Exception {
|
||||
// A non-restored widget with no config screen gets restored automatically.
|
||||
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
|
||||
|
||||
writeLayout(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
|
||||
.putWidget(info.getComponent().getPackageName(),
|
||||
info.getComponent().getClassName(), 2, 2));
|
||||
|
||||
// Launch the home activity
|
||||
mDevice.pressHome();
|
||||
|
||||
// Verify widget present
|
||||
assertTrue("Widget is not present",
|
||||
mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomProfileLoaded_with_folder() throws Exception {
|
||||
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
|
||||
.addApp(SETTINGS_APP, SETTINGS_APP)
|
||||
.addApp(SETTINGS_APP, SETTINGS_APP)
|
||||
.addApp(SETTINGS_APP, SETTINGS_APP)
|
||||
.build());
|
||||
|
||||
// Launch the home activity
|
||||
mDevice.pressHome();
|
||||
|
||||
mLauncher.getWorkspace().getHotseatFolder("Folder: Copy");
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() throws Exception {
|
||||
mDevice.executeShellCommand("settings delete secure launcher3.layout.provider");
|
||||
}
|
||||
|
||||
private void writeLayout(LauncherLayoutBuilder builder) throws Exception {
|
||||
mDevice.executeShellCommand("settings put secure launcher3.layout.provider " + mAuthority);
|
||||
ParcelFileDescriptor pfd = mTargetContext.getContentResolver().openFileDescriptor(
|
||||
Uri.parse("content://" + mAuthority + "/launcher_layout"), "w");
|
||||
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(new AutoCloseOutputStream(pfd))) {
|
||||
builder.build(writer);
|
||||
}
|
||||
clearLauncherData();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user