Merge "Launcher shortcut callback should deliver manifest shortcuts too" into nyc-mr1-dev

This commit is contained in:
Makoto Onuki
2016-06-20 21:06:40 +00:00
committed by Android (Google) Code Review
8 changed files with 342 additions and 29 deletions

View File

@@ -171,8 +171,9 @@ public class LauncherApps {
* as defined in {@link #hasShortcutHostPermission()}, will receive it.
*
* @param packageName The name of the package that has the shortcuts.
* @param shortcuts all shortcuts from the package (dynamic and/or pinned). Only "key"
* information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}.
* @param shortcuts all shortcuts from the package (dynamic, manifest and/or pinned).
* Only "key" information will be provided, as defined in
* {@link ShortcutInfo#hasKeyFieldsOnly()}.
* @param user The UserHandle of the profile that generated the change.
*/
public void onShortcutsChanged(@NonNull String packageName,
@@ -199,6 +200,10 @@ public class LauncherApps {
*/
public static final int FLAG_GET_MANIFEST = 1 << 3;
/** @hide */
public static final int FLAG_GET_ALL_KINDS =
FLAG_GET_DYNAMIC | FLAG_GET_PINNED | FLAG_GET_MANIFEST;
/**
* Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()} for which
* fields are available.
@@ -485,7 +490,7 @@ public class LauncherApps {
final ShortcutQuery q = new ShortcutQuery();
q.setPackage(packageName);
q.setShortcutIds(ids);
q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
return getShortcuts(q, user);
}
@@ -526,7 +531,7 @@ public class LauncherApps {
final ShortcutQuery q = new ShortcutQuery();
q.setPackage(packageName);
q.setShortcutIds(Arrays.asList(shortcutId));
q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
final List<ShortcutInfo> shortcuts = getShortcuts(q, user);
return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0;

View File

@@ -294,6 +294,7 @@ public final class ShortcutInfo implements Parcelable {
mUserId = source.mUserId;
mId = source.mId;
mPackageName = source.mPackageName;
mActivity = source.mActivity;
mFlags = source.mFlags;
mLastChangedTimestamp = source.mLastChangedTimestamp;
@@ -301,7 +302,6 @@ public final class ShortcutInfo implements Parcelable {
mIconResId = source.mIconResId;
if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
mActivity = source.mActivity;
if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
mIcon = source.mIcon;
@@ -1252,6 +1252,7 @@ public final class ShortcutInfo implements Parcelable {
* <ul>
* <li>{@link #getId()}
* <li>{@link #getPackage()}
* <li>{@link #getActivity()}
* <li>{@link #getLastChangedTimestamp()}
* <li>{@link #isDynamic()}
* <li>{@link #isPinned()}
@@ -1403,6 +1404,14 @@ public final class ShortcutInfo implements Parcelable {
mId = source.readString();
mPackageName = source.readString();
mActivity = source.readParcelable(cl);
mFlags = source.readInt();
mIconResId = source.readInt();
mLastChangedTimestamp = source.readLong();
if (source.readInt() == 0) {
return; // key information only.
}
mIcon = source.readParcelable(cl);
mTitle = source.readCharSequence();
mTitleResId = source.readInt();
@@ -1414,9 +1423,6 @@ public final class ShortcutInfo implements Parcelable {
mIntentPersistableExtras = source.readParcelable(cl);
mRank = source.readInt();
mExtras = source.readParcelable(cl);
mLastChangedTimestamp = source.readLong();
mFlags = source.readInt();
mIconResId = source.readInt();
mBitmapPath = source.readString();
mIconResName = source.readString();
@@ -1441,6 +1447,16 @@ public final class ShortcutInfo implements Parcelable {
dest.writeString(mId);
dest.writeString(mPackageName);
dest.writeParcelable(mActivity, flags);
dest.writeInt(mFlags);
dest.writeInt(mIconResId);
dest.writeLong(mLastChangedTimestamp);
if (hasKeyFieldsOnly()) {
dest.writeInt(0);
return;
}
dest.writeInt(1);
dest.writeParcelable(mIcon, flags);
dest.writeCharSequence(mTitle);
dest.writeInt(mTitleResId);
@@ -1453,9 +1469,6 @@ public final class ShortcutInfo implements Parcelable {
dest.writeParcelable(mIntentPersistableExtras, flags);
dest.writeInt(mRank);
dest.writeParcelable(mExtras, flags);
dest.writeLong(mLastChangedTimestamp);
dest.writeInt(mFlags);
dest.writeInt(mIconResId);
dest.writeString(mBitmapPath);
dest.writeString(mIconResName);

View File

@@ -775,8 +775,7 @@ public class LauncherAppsService extends SystemService {
/* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
/* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
| ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC
| ShortcutQuery.FLAG_GET_ALL_KINDS
, userId);
try {
listener.onShortcutChanged(user, packageName,

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
<shortcut
android:shortcutId="ms1"
android:enabled="true"
android:icon="@drawable/icon1"
android:shortcutShortLabel="@string/shortcut_title1"
android:shortcutLongLabel="@string/shortcut_text1"
android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
>
<intent
android:action="action1"
android:data="http://a.b.c/"
>
</intent>
<categories android:name="android.shortcut.conversation" />
<categories android:name="android.shortcut.media" />
</shortcut>
<shortcut
android:shortcutId="ms2"
android:enabled="true"
android:icon="@drawable/icon2"
android:shortcutShortLabel="@string/shortcut_title2"
android:shortcutLongLabel="@string/shortcut_text2"
android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
>
<intent
android:action="action2"
android:data="http://a.b.c/2"
>
</intent>
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="ms3"
android:enabled="true"
android:icon="@drawable/icon3"
android:shortcutShortLabel="@string/shortcut_title2"
>
<intent android:action="action3" />
</shortcut>
</shortcuts>

View File

@@ -532,7 +532,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
static {
QUERY_ALL.setQueryFlags(
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
ShortcutQuery.FLAG_GET_ALL_KINDS);
}
@Override
@@ -1434,7 +1434,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected static ShortcutQuery buildAllQuery(String packageName) {
final ShortcutQuery q = new ShortcutQuery();
q.setPackage(packageName);
q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
q.setQueryFlags(ShortcutQuery.FLAG_GET_ALL_KINDS);
return q;
}
@@ -1445,6 +1445,12 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
return q;
}
protected static ShortcutQuery buildQueryWithFlags(int queryFlags) {
final ShortcutQuery q = new ShortcutQuery();
q.setQueryFlags(queryFlags);
return q;
}
protected void backupAndRestore() {
int prevUid = mInjectedCallingUid;

View File

@@ -1102,6 +1102,117 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO More tests: pinned but dynamic.
}
public void testGetShortcuts_shortcutKinds() throws Exception {
// Create 3 manifest and 3 dynamic shortcuts
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_3);
updatePackageVersion(CALLING_PACKAGE_1, 1);
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
// Pin 2 and 3
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "ms3", "s2", "s3"),
HANDLE_USER_0);
});
// Remove ms3 and s3
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"))));
});
// Check their status.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertWith(getCallerShortcuts())
.haveIds("ms1", "ms2", "ms3", "s1", "s2", "s3")
.selectByIds("ms1", "ms2")
.areAllManifest()
.areAllImmutable()
.areAllNotDynamic()
.revertToOriginalList()
.selectByIds("ms3")
.areAllNotManifest()
.areAllImmutable()
.areAllDisabled()
.areAllNotDynamic()
.revertToOriginalList()
.selectByIds("s1", "s2")
.areAllNotManifest()
.areAllMutable()
.areAllDynamic()
.revertToOriginalList()
.selectByIds("s3")
.areAllNotManifest()
.areAllMutable()
.areAllEnabled()
.areAllNotDynamic()
.revertToOriginalList()
.selectByIds("s1", "ms1")
.areAllNotPinned()
.revertToOriginalList()
.selectByIds("s2", "s3", "ms2", "ms3")
.areAllPinned()
;
});
// Finally, actual tests.
runWithCaller(LAUNCHER_1, USER_0, () -> {
assertWith(mLauncherApps.getShortcuts(
buildQueryWithFlags(ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0))
.haveIds("s1", "s2");
assertWith(mLauncherApps.getShortcuts(
buildQueryWithFlags(ShortcutQuery.FLAG_GET_MANIFEST), HANDLE_USER_0))
.haveIds("ms1", "ms2");
assertWith(mLauncherApps.getShortcuts(
buildQueryWithFlags(ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0))
.haveIds("s2", "s3", "ms2", "ms3");
assertWith(mLauncherApps.getShortcuts(
buildQueryWithFlags(
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED
), HANDLE_USER_0))
.haveIds("s1", "s2", "s3", "ms2", "ms3");
assertWith(mLauncherApps.getShortcuts(
buildQueryWithFlags(
ShortcutQuery.FLAG_GET_MANIFEST | ShortcutQuery.FLAG_GET_PINNED
), HANDLE_USER_0))
.haveIds("ms1", "s2", "s3", "ms2", "ms3");
assertWith(mLauncherApps.getShortcuts(
buildQueryWithFlags(
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_MANIFEST
), HANDLE_USER_0))
.haveIds("ms1", "ms2", "s1", "s2");
assertWith(mLauncherApps.getShortcuts(
buildQueryWithFlags(
ShortcutQuery.FLAG_GET_ALL_KINDS
), HANDLE_USER_0))
.haveIds("ms1", "ms2", "ms3", "s1", "s2", "s3");
});
}
public void testGetShortcuts_resolveStrings() throws Exception {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
@@ -2223,6 +2334,12 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
public void testLauncherCallback() throws Throwable {
// Disable throttling for this test.
mService.updateConfigurationLocked(
ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=99999999,"
+ ConfigConstants.KEY_MAX_SHORTCUTS + "=99999999"
);
LauncherApps.Callback c0 = mock(LauncherApps.Callback.class);
// Set listeners
@@ -2243,8 +2360,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
shortcuts.capture(),
eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3");
assertWith(shortcuts.getValue())
.haveIds("s1", "s2", "s3")
.areAllWithKeyFieldsOnly()
.areAllDynamic();
// From different package.
reset(c0);
@@ -2259,8 +2378,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
shortcuts.capture(),
eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3");
assertWith(shortcuts.getValue())
.haveIds("s1", "s2", "s3")
.areAllWithKeyFieldsOnly()
.areAllDynamic();
// Different user, callback shouldn't be called.
reset(c0);
@@ -2289,8 +2410,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
shortcuts.capture(),
eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3", "s4");
assertWith(shortcuts.getValue())
.haveIds("s1", "s2", "s3", "s4")
.areAllWithKeyFieldsOnly()
.areAllDynamic();
// Test for remove
reset(c0);
@@ -2305,8 +2428,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
shortcuts.capture(),
eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s2", "s3", "s4");
assertWith(shortcuts.getValue())
.haveIds("s2", "s3", "s4")
.areAllWithKeyFieldsOnly()
.areAllDynamic();
// Test for update
reset(c0);
@@ -2322,8 +2447,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
shortcuts.capture(),
eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s2", "s3", "s4");
assertWith(shortcuts.getValue())
.haveIds("s2", "s3", "s4")
.areAllWithKeyFieldsOnly()
.areAllDynamic();
// Test for deleteAll
reset(c0);
@@ -2338,7 +2465,100 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
shortcuts.capture(),
eq(HANDLE_USER_0)
);
assertEquals(0, shortcuts.getValue().size());
assertWith(shortcuts.getValue())
.isEmpty();
// Update package1 with manifest shortcuts
reset(c0);
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
waitOnMainThread();
shortcuts = ArgumentCaptor.forClass(List.class);
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
eq(HANDLE_USER_0)
);
assertWith(shortcuts.getValue())
.areAllManifest()
.areAllWithKeyFieldsOnly()
.haveIds("ms1", "ms2");
// Make sure pinned shortcuts are passed too.
// 1. Add dynamic shortcuts.
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"))));
});
// 2. Pin some.
runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_0);
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
assertWith(getCallerShortcuts())
.haveIds("ms1", "ms2", "s1", "s2")
.areAllEnabled()
.selectByIds("ms1", "ms2")
.areAllManifest()
.revertToOriginalList()
.selectByIds("s1", "s2")
.areAllDynamic()
;
});
// 3 Update the app with no manifest shortcuts. (Pinned one will survive.)
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_0);
updatePackageVersion(CALLING_PACKAGE_1, 1);
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
reset(c0); // Check the callback for the next API call.
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
mManager.removeDynamicShortcuts(list("s2"));
assertWith(getCallerShortcuts())
.haveIds("ms2", "s1", "s2")
.selectByIds("ms2")
.areAllNotManifest()
.areAllPinned()
.areAllImmutable()
.areAllDisabled()
.revertToOriginalList()
.selectByIds("s1")
.areAllDynamic()
.areAllNotPinned()
.areAllEnabled()
.revertToOriginalList()
.selectByIds("s2")
.areAllNotDynamic()
.areAllPinned()
.areAllEnabled()
;
});
waitOnMainThread();
shortcuts = ArgumentCaptor.forClass(List.class);
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
eq(HANDLE_USER_0)
);
assertWith(shortcuts.getValue())
.haveIds("ms2", "s1", "s2")
.areAllWithKeyFieldsOnly();
// Remove CALLING_PACKAGE_2
reset(c0);
@@ -2353,7 +2573,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
shortcuts.capture(),
eq(HANDLE_USER_0)
);
assertEquals(0, shortcuts.getValue().size());
assertWith(shortcuts.getValue())
.isEmpty();
}
public void testLauncherCallback_crossProfile() throws Throwable {

View File

@@ -329,7 +329,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals("id", si.getId());
assertEquals(null, si.getActivity());
assertEquals(new ComponentName("a", "b"), si.getActivity());
assertEquals(null, si.getIcon());
assertEquals(null, si.getTitle());
assertEquals(null, si.getText());
@@ -445,7 +445,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals("id", si.getId());
assertEquals(null, si.getActivity());
assertEquals(new ComponentName("a", "b"), si.getActivity());
assertEquals(null, si.getIcon());
assertEquals(0, si.getTitleResId());
assertEquals(null, si.getTitleResName());

View File

@@ -806,11 +806,24 @@ public class ShortcutManagerTestUtils {
return this;
}
public ShortcutListAsserter areAllWithKeyFieldsOnly() {
forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.hasKeyFieldsOnly()));
return this;
}
public ShortcutListAsserter areAllNotWithKeyFieldsOnly() {
forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.hasKeyFieldsOnly()));
return this;
}
public ShortcutListAsserter forAllShortcuts(Consumer<ShortcutInfo> sa) {
boolean found = false;
for (int i = 0; i < mList.size(); i++) {
final ShortcutInfo si = mList.get(i);
found = true;
sa.accept(si);
}
assertTrue("No shortcuts found.", found);
return this;
}