summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2019-03-22 14:13:36 -0700
committerSunny Goyal <sunnygoyal@google.com>2019-04-10 12:06:17 -0700
commitc0f03d96656a3c70a26d3ec97e9e782fbdbe64b9 (patch)
tree8696c1cb8b375fc1efa50e8ae4fed2a5c51d1010 /tests
parente09e1f2253a9f20f1c91532956053a11b43ad355 (diff)
downloadandroid_packages_apps_Trebuchet-c0f03d96656a3c70a26d3ec97e9e782fbdbe64b9.tar.gz
android_packages_apps_Trebuchet-c0f03d96656a3c70a26d3ec97e9e782fbdbe64b9.tar.bz2
android_packages_apps_Trebuchet-c0f03d96656a3c70a26d3ec97e9e782fbdbe64b9.zip
Adding support for loading the default layout from a content provider
The autority of the provider should be set in secure settings: launcher3.layout.provider Bug: 127987071 Change-Id: Iccf2960aa6c0a5a8ff9621b13d8963d9daecb993
Diffstat (limited to 'tests')
-rw-r--r--tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java23
-rw-r--r--tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java138
-rw-r--r--tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java172
3 files changed, 333 insertions, 0 deletions
diff --git a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
index 0edb3d61b..fa23b8d5b 100644
--- a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
+++ b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
@@ -18,6 +18,7 @@ package com.android.launcher3.testcomponent;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.DONT_KILL_APP;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import android.app.Activity;
import android.app.ActivityManager;
@@ -28,6 +29,13 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Base64;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
import androidx.test.InstrumentationRegistry;
@@ -104,4 +112,19 @@ public class TestCommandReceiver extends ContentProvider {
Uri uri = Uri.parse("content://" + inst.getContext().getPackageName() + ".commands");
return inst.getTargetContext().getContentResolver().call(uri, command, arg, null);
}
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ String path = Base64.encodeToString(uri.getPath().getBytes(),
+ Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
+ File file = new File(getContext().getCacheDir(), path);
+ if (!file.exists()) {
+ // Create an empty file so that we can pass its descriptor
+ try {
+ file.createNewFile();
+ } catch (IOException e) { }
+ }
+
+ return ParcelFileDescriptor.open(file, MODE_READ_WRITE);
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
new file mode 100644
index 000000000..1efdee8ef
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
@@ -0,0 +1,138 @@
+/**
+ * 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 com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.testcomponent.TestCommandReceiver;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+
+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;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiSelector;
+
+@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,
+ TestCommandReceiver.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
+ mActivityMonitor.startLauncher();
+ waitForModelLoaded();
+
+ // Verify widget present
+ UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
+ .description(getSettingsApp().getLabel().toString());
+ assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
+ }
+
+ @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
+ mActivityMonitor.startLauncher();
+ waitForModelLoaded();
+
+ // Verify widget present
+ UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
+ .className(LauncherAppWidgetHostView.class).description(info.label);
+ assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
+ }
+
+ @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
+ mActivityMonitor.startLauncher();
+ waitForModelLoaded();
+
+ // Verify widget present
+ UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
+ .descriptionContains(mTargetContext.getString(android.R.string.copy));
+ assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
+ }
+
+ @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();
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java b/tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
new file mode 100644
index 000000000..d3659ebdc
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
@@ -0,0 +1,172 @@
+/**
+ * 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.util;
+
+
+import android.text.TextUtils;
+import android.util.Pair;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class to build xml for Launcher Layout
+ */
+public class LauncherLayoutBuilder {
+
+ // Object Tags
+ private static final String TAG_WORKSPACE = "workspace";
+ private static final String TAG_AUTO_INSTALL = "autoinstall";
+ private static final String TAG_FOLDER = "folder";
+ private static final String TAG_APPWIDGET = "appwidget";
+ private static final String TAG_EXTRA = "extra";
+
+ private static final String ATTR_CONTAINER = "container";
+ private static final String ATTR_RANK = "rank";
+
+ private static final String ATTR_PACKAGE_NAME = "packageName";
+ private static final String ATTR_CLASS_NAME = "className";
+ private static final String ATTR_TITLE = "title";
+ private static final String ATTR_SCREEN = "screen";
+
+ // x and y can be specified as negative integers, in which case -1 represents the
+ // last row / column, -2 represents the second last, and so on.
+ private static final String ATTR_X = "x";
+ private static final String ATTR_Y = "y";
+ private static final String ATTR_SPAN_X = "spanX";
+ private static final String ATTR_SPAN_Y = "spanY";
+
+ private static final String ATTR_CHILDREN = "children";
+
+
+ // Style attrs -- "Extra"
+ private static final String ATTR_KEY = "key";
+ private static final String ATTR_VALUE = "value";
+
+ private static final String CONTAINER_DESKTOP = "desktop";
+ private static final String CONTAINER_HOTSEAT = "hotseat";
+
+ private final ArrayList<Pair<String, HashMap<String, Object>>> mNodes = new ArrayList<>();
+
+ public Location atHotseat(int rank) {
+ Location l = new Location();
+ l.items.put(ATTR_CONTAINER, CONTAINER_HOTSEAT);
+ l.items.put(ATTR_RANK, Integer.toString(rank));
+ return l;
+ }
+
+ public Location atWorkspace(int x, int y, int screen) {
+ Location l = new Location();
+ l.items.put(ATTR_CONTAINER, CONTAINER_DESKTOP);
+ l.items.put(ATTR_X, Integer.toString(x));
+ l.items.put(ATTR_Y, Integer.toString(y));
+ l.items.put(ATTR_SCREEN, Integer.toString(screen));
+ return l;
+ }
+
+ public String build() throws IOException {
+ StringWriter writer = new StringWriter();
+ build(writer);
+ return writer.toString();
+ }
+
+ public void build(Writer writer) throws IOException {
+ XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(writer);
+
+ serializer.startDocument("UTF-8", true);
+ serializer.startTag(null, TAG_WORKSPACE);
+ writeNodes(serializer, mNodes);
+ serializer.endTag(null, TAG_WORKSPACE);
+ serializer.endDocument();
+ serializer.flush();
+ }
+
+ private static void writeNodes(XmlSerializer serializer,
+ ArrayList<Pair<String, HashMap<String, Object>>> nodes) throws IOException {
+ for (Pair<String, HashMap<String, Object>> node : nodes) {
+ ArrayList<Pair<String, HashMap<String, Object>>> children = null;
+
+ serializer.startTag(null, node.first);
+ for (Map.Entry<String, Object> attr : node.second.entrySet()) {
+ if (ATTR_CHILDREN.equals(attr.getKey())) {
+ children = (ArrayList<Pair<String, HashMap<String, Object>>>) attr.getValue();
+ } else {
+ serializer.attribute(null, attr.getKey(), (String) attr.getValue());
+ }
+ }
+
+ if (children != null) {
+ writeNodes(serializer, children);
+ }
+ serializer.endTag(null, node.first);
+ }
+ }
+
+ public class Location {
+
+ final HashMap<String, Object> items = new HashMap<>();
+
+ public LauncherLayoutBuilder putApp(String packageName, String className) {
+ items.put(ATTR_PACKAGE_NAME, packageName);
+ items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
+ mNodes.add(Pair.create(TAG_AUTO_INSTALL, items));
+ return LauncherLayoutBuilder.this;
+ }
+
+ public LauncherLayoutBuilder putWidget(String packageName, String className,
+ int spanX, int spanY) {
+ items.put(ATTR_PACKAGE_NAME, packageName);
+ items.put(ATTR_CLASS_NAME, className);
+ items.put(ATTR_SPAN_X, Integer.toString(spanX));
+ items.put(ATTR_SPAN_Y, Integer.toString(spanY));
+ mNodes.add(Pair.create(TAG_APPWIDGET, items));
+ return LauncherLayoutBuilder.this;
+ }
+
+ public FolderBuilder putFolder(int titleResId) {
+ FolderBuilder folderBuilder = new FolderBuilder();
+ items.put(ATTR_TITLE, Integer.toString(titleResId));
+ items.put(ATTR_CHILDREN, folderBuilder.mChildren);
+ mNodes.add(Pair.create(TAG_FOLDER, items));
+ return folderBuilder;
+ }
+ }
+
+ public class FolderBuilder {
+
+ final ArrayList<Pair<String, HashMap<String, Object>>> mChildren = new ArrayList<>();
+
+ public FolderBuilder addApp(String packageName, String className) {
+ HashMap<String, Object> items = new HashMap<>();
+ items.put(ATTR_PACKAGE_NAME, packageName);
+ items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
+ mChildren.add(Pair.create(TAG_AUTO_INSTALL, items));
+ return this;
+ }
+
+ public LauncherLayoutBuilder build() {
+ return LauncherLayoutBuilder.this;
+ }
+ }
+}