Follow-up to multiple intents support
- More unit tests - LauncherApps.startShortcut() now supports sourceBounds (again) - Updated the javadoc. Bug 30218829 Change-Id: Iae208ffd4911d149246ccfd0c4380544c2aafffc
This commit is contained in:
@@ -941,7 +941,8 @@ public final class ShortcutInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the intent of a shortcut.
|
||||
* Sets the intent of a shortcut. Alternatively, {@link #setIntents(Intent[])} can be used
|
||||
* to launch an activity with other activities in the back stack.
|
||||
*
|
||||
* <p>This is a mandatory field when publishing a new shortcut with
|
||||
* {@link ShortcutManager#addDynamicShortcuts(List)} or
|
||||
@@ -965,7 +966,9 @@ public final class ShortcutInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets multiple intents instead of a single intent.
|
||||
* Sets multiple intents instead of a single intent, in order to launch an activity with
|
||||
* other activities in back stack. Use {@link TaskStackBuilder} to build intents.
|
||||
* See the {@link ShortcutManager} javadoc for details.
|
||||
*
|
||||
* @see Builder#setIntent(Intent)
|
||||
* @see ShortcutInfo#getIntents()
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.content.pm;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.TestApi;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.Activity;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -30,7 +31,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ShortcutManager manages "launcher shortcuts" (or simply "shortcuts"). Shortcuts provide user
|
||||
* ShortcutManager manages "launcher shortcuts" (or simply "shortcuts"). Shortcuts provide users
|
||||
* with quick
|
||||
* ways to access activities other than the main activity from the launcher to users. For example,
|
||||
* an email application may publish the "compose new email" action which will directly open the
|
||||
@@ -183,6 +184,7 @@ import java.util.List;
|
||||
* android:action="android.intent.action.VIEW"
|
||||
* android:targetPackage="com.example.myapplication"
|
||||
* android:targetClass="com.example.myapplication.ComposeActivity" />
|
||||
* <!-- more intents can go here; see below -->
|
||||
* <categories android:name="android.shortcut.conversation" />
|
||||
* </shortcut>
|
||||
* <!-- more shortcut can go here -->
|
||||
@@ -209,9 +211,37 @@ import java.util.List;
|
||||
*
|
||||
* <li>{@code intent} Intent to launch. {@code android:action} is mandatory.
|
||||
* See <a href="{@docRoot}guide/topics/ui/settings.html#Intents">Using intents</a> for the
|
||||
* other supported tags.
|
||||
* other supported tags. Multiple intents can be provided for a single shortcut, so that
|
||||
* an activity will be launched with other activities in the back stack.
|
||||
* See {@link android.app.TaskStackBuilder} for details.
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Shortcut Intents</h3>
|
||||
* Dynamic shortcuts can be published with any {@link Intent#addFlags Intent flags}. Typically,
|
||||
* {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} is specified possibly with other flags; otherwise,
|
||||
* if the application is already running, the application is simply brought to the foreground
|
||||
* and the target activity may not show up.
|
||||
*
|
||||
* <p>{@link ShortcutInfo.Builder#setIntents(Intent[])} can be used (instead of
|
||||
* {@link ShortcutInfo.Builder#setIntent(Intent)}) with
|
||||
* {@link android.app.TaskStackBuilder} in order to launch an activity with other activities
|
||||
* in the back stack, so that when the user presses the back key, a "parent" activity will be shown
|
||||
* instead of the user being navigated back to the launcher.
|
||||
*
|
||||
* <p>Manifest shortcuts can have multiple intents too to achieve the same effect. In order to
|
||||
* specify multiple {@link Intent}s to a shortcut, simply list multiple <intent>s within
|
||||
* a single <shortcut>. The last intent is what the user will see when a shortcut is
|
||||
* launched.
|
||||
*
|
||||
* <p>Manifest shortcuts <b>cannot</b> have custom intent flags. The first intent of a manifest
|
||||
* shortcut will always have {@link Intent#FLAG_ACTIVITY_NEW_TASK} and
|
||||
* {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} set. This means, when the application is already
|
||||
* running, all the existing activities will be destroyed when a manifest shortcut is launched.
|
||||
* If this behavior is not desirable, one can use a "trampoline" activity (an activity
|
||||
* that starts another activity in {@link Activity#onCreate} and then calls
|
||||
* {@link Activity#finish()}) with {@code android:taskAffinity=""} in AndroidManifest.xml and point
|
||||
* at this activity in a manifest shortcut's intent.
|
||||
*
|
||||
* <h3>Updating Shortcuts v.s. Re-publishing New One with Different ID</h3>
|
||||
* In order to avoid users' confusion, {@link #updateShortcuts(List)} should not be used to update
|
||||
* a shortcut to something that is conceptually different.
|
||||
@@ -267,6 +297,8 @@ import java.util.List;
|
||||
* <li>When the user performs "inline reply" on a notification.
|
||||
* </ul>
|
||||
*
|
||||
* <p>When rate-limiting is active, {@link #isRateLimitingActive()} returns {@code true}.
|
||||
*
|
||||
* <h4>Resetting rate-limiting for testing</h4>
|
||||
*
|
||||
* If your application is rate-limited during development or testing, you can use the
|
||||
|
||||
@@ -459,9 +459,8 @@ public class LauncherAppsService extends SystemService {
|
||||
}
|
||||
// Note the target activity doesn't have to be exported.
|
||||
|
||||
// TODO Use sourceBounds
|
||||
|
||||
intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intents[0].setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intents[0].setSourceBounds(sourceBounds);
|
||||
|
||||
return startShortcutIntentsAsPublisher(
|
||||
intents, packageName, startActivityOptions, userId);
|
||||
|
||||
@@ -842,12 +842,17 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
getLastResetTimeLocked();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
final File getUserFile(@UserIdInt int userId) {
|
||||
return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
|
||||
}
|
||||
|
||||
private void saveUserLocked(@UserIdInt int userId) {
|
||||
final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
|
||||
final File path = getUserFile(userId);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Saving to " + path);
|
||||
}
|
||||
path.mkdirs();
|
||||
path.getParentFile().mkdirs();
|
||||
final AtomicFile file = new AtomicFile(path);
|
||||
FileOutputStream os = null;
|
||||
try {
|
||||
@@ -890,7 +895,7 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
|
||||
@Nullable
|
||||
private ShortcutUser loadUserLocked(@UserIdInt int userId) {
|
||||
final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
|
||||
final File path = getUserFile(userId);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Loading from " + path);
|
||||
}
|
||||
@@ -1466,7 +1471,7 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
* Clean up / validate an incoming shortcut.
|
||||
* - Make sure all mandatory fields are set.
|
||||
* - Make sure the intent's extras are persistable, and them to set
|
||||
* {@link ShortcutInfo#mIntentPersistableExtras}. Also clear its extras.
|
||||
* {@link ShortcutInfo#mIntentPersistableExtrases}. Also clear its extras.
|
||||
* - Clear flags.
|
||||
*
|
||||
* TODO Detailed unit tests
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<user locales="en-US" last-app-scan-time="3113976673">
|
||||
<package name="com.android.test.1" call-count="0" last-reset="1468976368772">
|
||||
<package-info version="25" last_udpate_time="1230796800000" />
|
||||
<shortcut id="manifest-shortcut-storage" activity="com.android.test.1/com.android.test.1.Settings" title="Storage" titleid="2131625197" titlename="storage_settings" textid="0" dmessageid="0" intent="#Intent;action=android.settings.INTERNAL_STORAGE_SETTINGS;end" timestamp="1469050672334" rank="4" flags="420" icon-res="2130837747" icon-resname="drawable/ic_shortcut_storage" >
|
||||
<intent-extras>
|
||||
<int name="key" value="12345" />
|
||||
</intent-extras>
|
||||
</shortcut>
|
||||
</package>
|
||||
</user>
|
||||
@@ -48,9 +48,11 @@ import com.android.frameworks.servicestests.R;
|
||||
import com.android.server.pm.ShortcutService.ConfigConstants;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
@@ -98,6 +100,12 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
"action must be set",
|
||||
() -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(new Intent()));
|
||||
|
||||
assertExpectException(
|
||||
RuntimeException.class,
|
||||
"action must be set",
|
||||
() -> new ShortcutInfo.Builder(getTestContext(), "id")
|
||||
.setIntents(new Intent[]{new Intent("action"), new Intent()}));
|
||||
|
||||
assertExpectException(
|
||||
RuntimeException.class,
|
||||
"activity cannot be null",
|
||||
@@ -1967,4 +1975,28 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
|
||||
mService.dump(null, new PrintWriter(new StringWriter()), null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the legacy file format that only supported a single intent per shortcut
|
||||
* can still be read.
|
||||
*/
|
||||
public void testLoadLegacySavedFile() throws Exception {
|
||||
final File path = mService.getUserFile(USER_0);
|
||||
path.getParentFile().mkdirs();
|
||||
try (Writer w = new FileWriter(path)) {
|
||||
w.write(readTestAsset("shortcut/shortcut_legacy_file.xml"));
|
||||
};
|
||||
initService();
|
||||
mService.handleUnlockUser(USER_0);
|
||||
|
||||
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
|
||||
assertWith(getCallerShortcuts())
|
||||
.haveIds("manifest-shortcut-storage")
|
||||
.forShortcutWithId("manifest-shortcut-storage", si -> {
|
||||
assertEquals("android.settings.INTERNAL_STORAGE_SETTINGS",
|
||||
si.getIntent().getAction());
|
||||
assertEquals(12345, si.getIntent().getIntExtra("key", 0));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user