2017-01-21 01:33:02 -08:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2017 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.widget;
|
2016-03-10 05:34:30 -08:00
|
|
|
|
2019-09-18 22:29:40 -07:00
|
|
|
import static androidx.test.InstrumentationRegistry.getTargetContext;
|
|
|
|
|
|
2021-07-29 15:48:24 -07:00
|
|
|
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
|
2019-06-25 18:34:48 -07:00
|
|
|
|
2018-08-10 20:01:24 -07:00
|
|
|
import static org.junit.Assert.assertEquals;
|
|
|
|
|
import static org.junit.Assert.assertNotNull;
|
2020-02-24 17:06:28 -08:00
|
|
|
import static org.junit.Assert.assertNull;
|
2018-08-10 20:01:24 -07:00
|
|
|
import static org.junit.Assert.assertTrue;
|
|
|
|
|
|
2017-08-16 04:59:08 -07:00
|
|
|
import android.appwidget.AppWidgetManager;
|
2016-03-10 05:34:30 -08:00
|
|
|
import android.content.ComponentName;
|
|
|
|
|
import android.content.ContentResolver;
|
|
|
|
|
import android.content.pm.PackageInstaller;
|
|
|
|
|
import android.content.pm.PackageInstaller.SessionParams;
|
|
|
|
|
import android.content.pm.PackageManager;
|
|
|
|
|
import android.database.Cursor;
|
|
|
|
|
import android.os.Bundle;
|
2020-02-24 17:06:28 -08:00
|
|
|
import android.widget.RemoteViews;
|
2016-03-10 05:34:30 -08:00
|
|
|
|
2019-06-25 18:34:48 -07:00
|
|
|
import androidx.test.filters.LargeTest;
|
|
|
|
|
import androidx.test.runner.AndroidJUnit4;
|
|
|
|
|
|
2017-01-21 01:33:02 -08:00
|
|
|
import com.android.launcher3.LauncherSettings;
|
2020-02-24 17:06:28 -08:00
|
|
|
import com.android.launcher3.R;
|
2020-04-06 15:11:17 -07:00
|
|
|
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
|
2019-12-09 14:55:56 -08:00
|
|
|
import com.android.launcher3.pm.InstallSessionHelper;
|
2020-03-27 15:08:36 -07:00
|
|
|
import com.android.launcher3.tapl.Widget;
|
2019-06-25 18:34:48 -07:00
|
|
|
import com.android.launcher3.tapl.Workspace;
|
2017-07-31 10:59:52 -07:00
|
|
|
import com.android.launcher3.ui.AbstractLauncherUiTest;
|
2018-10-12 11:21:58 -07:00
|
|
|
import com.android.launcher3.ui.TestViewHelpers;
|
2017-07-31 10:59:52 -07:00
|
|
|
import com.android.launcher3.util.rule.ShellCommandRule;
|
2021-02-22 14:03:44 +00:00
|
|
|
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
2019-12-10 12:19:13 -08:00
|
|
|
import com.android.launcher3.widget.WidgetManagerHelper;
|
2016-03-10 05:34:30 -08:00
|
|
|
|
2017-07-31 10:59:52 -07:00
|
|
|
import org.junit.After;
|
|
|
|
|
import org.junit.Before;
|
|
|
|
|
import org.junit.Rule;
|
|
|
|
|
import org.junit.Test;
|
|
|
|
|
import org.junit.runner.RunWith;
|
|
|
|
|
|
2019-09-16 21:38:28 -07:00
|
|
|
import java.util.HashSet;
|
2016-03-10 05:34:30 -08:00
|
|
|
import java.util.Set;
|
2018-10-11 15:51:39 -07:00
|
|
|
|
2016-03-10 05:34:30 -08:00
|
|
|
/**
|
|
|
|
|
* Tests for bind widget flow.
|
|
|
|
|
*
|
|
|
|
|
* Note running these tests will clear the workspace on the device.
|
|
|
|
|
*/
|
2016-05-05 11:46:03 -07:00
|
|
|
@LargeTest
|
2017-07-31 10:59:52 -07:00
|
|
|
@RunWith(AndroidJUnit4.class)
|
|
|
|
|
public class BindWidgetTest extends AbstractLauncherUiTest {
|
|
|
|
|
|
2018-10-12 11:21:58 -07:00
|
|
|
@Rule
|
|
|
|
|
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
|
2018-10-11 15:51:39 -07:00
|
|
|
|
2016-03-10 05:34:30 -08:00
|
|
|
private ContentResolver mResolver;
|
|
|
|
|
|
|
|
|
|
// Objects created during test, which should be cleaned up in the end.
|
|
|
|
|
private Cursor mCursor;
|
|
|
|
|
// App install session id.
|
|
|
|
|
private int mSessionId = -1;
|
|
|
|
|
|
|
|
|
|
@Override
|
2017-07-31 10:59:52 -07:00
|
|
|
@Before
|
|
|
|
|
public void setUp() throws Exception {
|
2016-03-10 05:34:30 -08:00
|
|
|
super.setUp();
|
|
|
|
|
|
|
|
|
|
mResolver = mTargetContext.getContentResolver();
|
|
|
|
|
|
|
|
|
|
// Clear all existing data
|
|
|
|
|
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
|
2020-02-24 17:06:28 -08:00
|
|
|
LauncherSettings.Settings.call(mResolver,
|
|
|
|
|
LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
|
2016-03-10 05:34:30 -08:00
|
|
|
}
|
|
|
|
|
|
2017-07-31 10:59:52 -07:00
|
|
|
@After
|
2019-04-03 16:03:15 -07:00
|
|
|
public void tearDown() {
|
2016-03-10 05:34:30 -08:00
|
|
|
if (mCursor != null) {
|
|
|
|
|
mCursor.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mSessionId > -1) {
|
|
|
|
|
mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-31 10:59:52 -07:00
|
|
|
@Test
|
2016-03-10 05:34:30 -08:00
|
|
|
public void testBindNormalWidget_withConfig() {
|
2018-10-12 11:21:58 -07:00
|
|
|
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
|
2020-04-20 14:41:23 -07:00
|
|
|
LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true);
|
2016-03-10 05:34:30 -08:00
|
|
|
|
2019-09-18 22:29:40 -07:00
|
|
|
addItemToScreen(item);
|
2018-10-11 15:51:39 -07:00
|
|
|
verifyWidgetPresent(info);
|
2016-03-10 05:34:30 -08:00
|
|
|
}
|
|
|
|
|
|
2017-07-31 10:59:52 -07:00
|
|
|
@Test
|
2016-03-10 05:34:30 -08:00
|
|
|
public void testBindNormalWidget_withoutConfig() {
|
2018-10-12 11:21:58 -07:00
|
|
|
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
|
2020-04-20 14:41:23 -07:00
|
|
|
LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true);
|
2016-03-10 05:34:30 -08:00
|
|
|
|
2019-09-18 22:29:40 -07:00
|
|
|
addItemToScreen(item);
|
2018-10-11 15:51:39 -07:00
|
|
|
verifyWidgetPresent(info);
|
2016-03-10 05:34:30 -08:00
|
|
|
}
|
|
|
|
|
|
2019-05-03 12:56:13 -07:00
|
|
|
@Test
|
2018-10-11 15:51:39 -07:00
|
|
|
public void testUnboundWidget_removed() {
|
2018-10-12 11:21:58 -07:00
|
|
|
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
|
2020-04-20 14:41:23 -07:00
|
|
|
LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false);
|
2016-03-18 17:42:55 -07:00
|
|
|
item.appWidgetId = -33;
|
2016-03-10 05:34:30 -08:00
|
|
|
|
2019-09-18 22:29:40 -07:00
|
|
|
addItemToScreen(item);
|
2016-03-10 05:34:30 -08:00
|
|
|
|
2019-06-25 18:34:48 -07:00
|
|
|
final Workspace workspace = mLauncher.getWorkspace();
|
2016-03-10 05:34:30 -08:00
|
|
|
// Item deleted from db
|
|
|
|
|
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
|
|
|
|
null, null, null, null, null);
|
|
|
|
|
assertEquals(0, mCursor.getCount());
|
|
|
|
|
|
|
|
|
|
// The view does not exist
|
2019-06-25 18:34:48 -07:00
|
|
|
assertTrue("Widget exists", workspace.tryGetWidget(info.label, 0) == null);
|
2016-03-10 05:34:30 -08:00
|
|
|
}
|
|
|
|
|
|
2019-05-03 12:56:13 -07:00
|
|
|
@Test
|
2016-03-10 05:34:30 -08:00
|
|
|
public void testPendingWidget_autoRestored() {
|
|
|
|
|
// A non-restored widget with no config screen gets restored automatically.
|
2018-10-12 11:21:58 -07:00
|
|
|
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
|
2016-03-10 05:34:30 -08:00
|
|
|
|
|
|
|
|
// Do not bind the widget
|
2020-04-20 14:41:23 -07:00
|
|
|
LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false);
|
2016-03-10 05:34:30 -08:00
|
|
|
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
|
|
|
|
|
|
2019-09-18 22:29:40 -07:00
|
|
|
addItemToScreen(item);
|
2018-10-11 15:51:39 -07:00
|
|
|
verifyWidgetPresent(info);
|
2016-03-10 05:34:30 -08:00
|
|
|
}
|
|
|
|
|
|
2017-07-31 10:59:52 -07:00
|
|
|
@Test
|
2018-10-11 15:51:39 -07:00
|
|
|
public void testPendingWidget_withConfigScreen() {
|
2016-03-10 05:34:30 -08:00
|
|
|
// A non-restored widget with config screen get bound and shows a 'Click to setup' UI.
|
2018-10-12 11:21:58 -07:00
|
|
|
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
|
2016-03-10 05:34:30 -08:00
|
|
|
|
|
|
|
|
// Do not bind the widget
|
2020-04-20 14:41:23 -07:00
|
|
|
LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), false);
|
2016-03-10 05:34:30 -08:00
|
|
|
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
|
|
|
|
|
|
2019-09-18 22:29:40 -07:00
|
|
|
addItemToScreen(item);
|
2018-10-11 15:51:39 -07:00
|
|
|
verifyPendingWidgetPresent();
|
|
|
|
|
|
2016-03-10 05:34:30 -08:00
|
|
|
// Item deleted from db
|
|
|
|
|
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
|
|
|
|
null, null, null, null, null);
|
|
|
|
|
mCursor.moveToNext();
|
|
|
|
|
|
|
|
|
|
// Widget has a valid Id now.
|
|
|
|
|
assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
|
|
|
|
|
& LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
|
2017-08-16 04:59:08 -07:00
|
|
|
assertNotNull(AppWidgetManager.getInstance(mTargetContext)
|
|
|
|
|
.getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex(
|
|
|
|
|
LauncherSettings.Favorites.APPWIDGET_ID))));
|
2020-02-24 17:06:28 -08:00
|
|
|
|
|
|
|
|
// send OPTION_APPWIDGET_RESTORE_COMPLETED
|
|
|
|
|
int appWidgetId = mCursor.getInt(
|
|
|
|
|
mCursor.getColumnIndex(LauncherSettings.Favorites.APPWIDGET_ID));
|
|
|
|
|
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mTargetContext);
|
|
|
|
|
|
|
|
|
|
Bundle b = new Bundle();
|
|
|
|
|
b.putBoolean(WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED, true);
|
|
|
|
|
RemoteViews remoteViews = new RemoteViews(mTargetPackage, R.layout.appwidget_not_ready);
|
|
|
|
|
appWidgetManager.updateAppWidgetOptions(appWidgetId, b);
|
|
|
|
|
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// verify changes are reflected
|
|
|
|
|
waitForLauncherCondition("App widget options did not update",
|
|
|
|
|
l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean(
|
|
|
|
|
WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
|
|
|
|
|
executeOnLauncher(l -> l.getAppWidgetHost().startListening());
|
|
|
|
|
verifyWidgetPresent(info);
|
2020-07-23 16:05:02 -07:00
|
|
|
assertNull(mLauncher.getWorkspace().tryGetPendingWidget(100));
|
2016-03-10 05:34:30 -08:00
|
|
|
}
|
|
|
|
|
|
2019-05-03 12:56:13 -07:00
|
|
|
@Test
|
2018-10-11 15:51:39 -07:00
|
|
|
public void testPendingWidget_notRestored_removed() {
|
2016-03-10 05:34:30 -08:00
|
|
|
LauncherAppWidgetInfo item = getInvalidWidgetInfo();
|
|
|
|
|
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
|
|
|
|
|
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
|
|
|
|
|
|
2019-09-18 22:29:40 -07:00
|
|
|
addItemToScreen(item);
|
2018-10-11 15:51:39 -07:00
|
|
|
|
2019-06-25 18:34:48 -07:00
|
|
|
assertTrue("Pending widget exists",
|
|
|
|
|
mLauncher.getWorkspace().tryGetPendingWidget(0) == null);
|
2016-03-10 05:34:30 -08:00
|
|
|
// Item deleted from db
|
|
|
|
|
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
|
|
|
|
null, null, null, null, null);
|
|
|
|
|
assertEquals(0, mCursor.getCount());
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-31 10:59:52 -07:00
|
|
|
@Test
|
2018-10-11 15:51:39 -07:00
|
|
|
public void testPendingWidget_notRestored_brokenInstall() {
|
2016-03-10 05:34:30 -08:00
|
|
|
// A widget which is was being installed once, even if its not being
|
|
|
|
|
// installed at the moment is not removed.
|
|
|
|
|
LauncherAppWidgetInfo item = getInvalidWidgetInfo();
|
|
|
|
|
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
|
|
|
|
|
| LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
|
|
|
|
|
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
|
|
|
|
|
|
2019-09-18 22:29:40 -07:00
|
|
|
addItemToScreen(item);
|
2018-10-11 15:51:39 -07:00
|
|
|
verifyPendingWidgetPresent();
|
|
|
|
|
|
2016-03-10 05:34:30 -08:00
|
|
|
// Verify item still exists in db
|
|
|
|
|
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
|
|
|
|
null, null, null, null, null);
|
|
|
|
|
assertEquals(1, mCursor.getCount());
|
|
|
|
|
|
|
|
|
|
// Widget still has an invalid id.
|
|
|
|
|
mCursor.moveToNext();
|
|
|
|
|
assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID,
|
|
|
|
|
mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
|
|
|
|
|
& LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-31 10:59:52 -07:00
|
|
|
@Test
|
2016-03-10 05:34:30 -08:00
|
|
|
public void testPendingWidget_notRestored_activeInstall() throws Exception {
|
|
|
|
|
// A widget which is being installed is not removed
|
|
|
|
|
LauncherAppWidgetInfo item = getInvalidWidgetInfo();
|
|
|
|
|
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
|
|
|
|
|
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
|
|
|
|
|
|
|
|
|
|
// Create an active installer session
|
|
|
|
|
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
|
|
|
|
|
params.setAppPackageName(item.providerName.getPackageName());
|
|
|
|
|
PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
|
|
|
|
|
mSessionId = installer.createSession(params);
|
|
|
|
|
|
2019-09-18 22:29:40 -07:00
|
|
|
addItemToScreen(item);
|
2018-10-11 15:51:39 -07:00
|
|
|
verifyPendingWidgetPresent();
|
|
|
|
|
|
2016-03-10 05:34:30 -08:00
|
|
|
// Verify item still exists in db
|
|
|
|
|
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
|
|
|
|
null, null, null, null, null);
|
|
|
|
|
assertEquals(1, mCursor.getCount());
|
|
|
|
|
|
|
|
|
|
// Widget still has an invalid id.
|
|
|
|
|
mCursor.moveToNext();
|
|
|
|
|
assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID,
|
|
|
|
|
mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
|
|
|
|
|
& LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-11 15:51:39 -07:00
|
|
|
private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
|
2020-03-27 15:08:36 -07:00
|
|
|
final Widget widget = mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT);
|
2019-06-25 18:34:48 -07:00
|
|
|
assertTrue("Widget is not present",
|
2020-03-27 15:08:36 -07:00
|
|
|
widget != null);
|
2018-10-11 15:51:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void verifyPendingWidgetPresent() {
|
2020-04-01 15:33:35 -07:00
|
|
|
final Widget widget = mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT);
|
2019-06-25 18:34:48 -07:00
|
|
|
assertTrue("Pending widget is not present",
|
2020-04-01 15:33:35 -07:00
|
|
|
widget != null);
|
2016-03-10 05:34:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a LauncherAppWidgetInfo with package name which is not present on the device
|
|
|
|
|
*/
|
|
|
|
|
private LauncherAppWidgetInfo getInvalidWidgetInfo() {
|
|
|
|
|
String invalidPackage = "com.invalidpackage";
|
|
|
|
|
int count = 0;
|
|
|
|
|
String pkg = invalidPackage;
|
|
|
|
|
|
2019-09-16 21:38:28 -07:00
|
|
|
Set<String> activePackage = getOnUiThread(() -> {
|
|
|
|
|
Set<String> packages = new HashSet<>();
|
2019-12-09 14:55:56 -08:00
|
|
|
InstallSessionHelper.INSTANCE.get(mTargetContext).getActiveSessions()
|
2019-09-16 21:38:28 -07:00
|
|
|
.keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName));
|
|
|
|
|
return packages;
|
|
|
|
|
});
|
2020-02-24 17:06:28 -08:00
|
|
|
while (true) {
|
2016-03-10 05:34:30 -08:00
|
|
|
try {
|
|
|
|
|
mTargetContext.getPackageManager().getPackageInfo(
|
|
|
|
|
pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
if (!activePackage.contains(pkg)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pkg = invalidPackage + count;
|
2020-02-24 17:06:28 -08:00
|
|
|
count++;
|
2016-03-10 05:34:30 -08:00
|
|
|
}
|
|
|
|
|
LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(10,
|
|
|
|
|
new ComponentName(pkg, "com.test.widgetprovider"));
|
|
|
|
|
item.spanX = 2;
|
|
|
|
|
item.spanY = 2;
|
|
|
|
|
item.minSpanX = 2;
|
|
|
|
|
item.minSpanY = 2;
|
|
|
|
|
item.cellX = 0;
|
2016-05-26 16:05:17 -07:00
|
|
|
item.cellY = 1;
|
2016-03-10 05:34:30 -08:00
|
|
|
item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
}
|