AppWidgetManager: direct add widget support.

Test: Manual test and all the unit tests:
adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest1 -w com.android.frameworks.servicestests
... to test9
adb shell am instrument -e class com.android.server.appwidget.AppWidgetServiceImplTest -w com.android.frameworks.servicestests

Bug 32404406
Change-Id: Icd6d4cbd25d9cdf4508da725d95d6401cc3a46a7
This commit is contained in:
Sunny Goyal
2017-01-01 19:42:45 -08:00
parent 564e802a7c
commit 87a563e070
18 changed files with 582 additions and 56 deletions

View File

@@ -16,11 +16,15 @@
package android.appwidget;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
@@ -1079,4 +1083,41 @@ public class AppWidgetManager {
info.minResizeHeight = TypedValue.complexToDimensionPixelSize(info.minResizeHeight,
mDisplayMetrics);
}
/**
* Request to pin an app widget on the current launcher. It's up to the launcher to accept this
* request (optionally showing a user confirmation). If the request is accepted, the caller will
* get a confirmation with extra {@link #EXTRA_APPWIDGET_ID}.
*
* <p>When a request is denied by the user, the caller app will not get any response.
*
* <p>Only apps with a foreground activity or a foreground service can call it. Otherwise
* it'll throw {@link IllegalStateException}.
*
* <p>When an app calls this API when a previous request is still waiting for a response,
* the previous request will be canceled.
*
* @param provider The {@link ComponentName} for the {@link
* android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
* @param successCallback If not null, this intent will be sent when the widget is created.
*
* @return {@code TRUE} if the launcher supports this feature. Note the API will return without
* waiting for the user to respond, so getting {@code TRUE} from this API does *not* mean
* the shortcut is pinned. {@code FALSE} if the launcher doesn't support this feature.
*
* @see android.content.pm.ShortcutManager#isRequestPinShortcutSupported()
* @see android.content.pm.ShortcutManager#requestPinShortcut(ShortcutInfo, IntentSender)
*
* @throws IllegalStateException The caller doesn't have a foreground activity or a foreground
* service or when the user is locked.
*/
public boolean requestPinAppWidget(@NonNull ComponentName provider,
@Nullable PendingIntent successCallback) {
try {
return mService.requestPinAppWidget(mPackageName, provider,
successCallback == null ? null : successCallback.getIntentSender());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.TestApi;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -1138,21 +1139,35 @@ public class LauncherApps {
/** This is a request to pin shortcut. */
public static final int REQUEST_TYPE_SHORTCUT = 1;
/** This is a request to pin app widget. */
public static final int REQUEST_TYPE_APPWIDGET = 2;
@IntDef(value = {REQUEST_TYPE_SHORTCUT})
@Retention(RetentionPolicy.SOURCE)
public @interface RequestType {}
private final int mRequestType;
private final ShortcutInfo mShortcutInfo;
private final AppWidgetProviderInfo mAppWidgetInfo;
private final IPinItemRequest mInner;
/**
* @hide
*/
public PinItemRequest(@RequestType int requestType, ShortcutInfo shortcutInfo,
IPinItemRequest inner) {
mRequestType = requestType;
public PinItemRequest(ShortcutInfo shortcutInfo, IPinItemRequest inner) {
mRequestType = REQUEST_TYPE_SHORTCUT;
mShortcutInfo = shortcutInfo;
mAppWidgetInfo = null;
mInner = inner;
}
/**
* @hide
*/
public PinItemRequest(AppWidgetProviderInfo appWidgetInfo, IPinItemRequest inner) {
mRequestType = REQUEST_TYPE_APPWIDGET;
mShortcutInfo = null;
mAppWidgetInfo = appWidgetInfo;
mInner = inner;
}
@@ -1174,6 +1189,15 @@ public class LauncherApps {
return mShortcutInfo;
}
/**
* {@link AppWidgetProviderInfo} sent by the requesting app. Always non-null for a
* {@link #REQUEST_TYPE_APPWIDGET} request.
*/
@Nullable
public AppWidgetProviderInfo getAppWidgetProviderInfo() {
return mAppWidgetInfo;
}
/**
* Return {@code TRUE} if a request is valid -- i.e. {@link #accept(Bundle)} has not been
* called, and it has not been canceled.
@@ -1208,14 +1232,22 @@ public class LauncherApps {
final ClassLoader cl = getClass().getClassLoader();
mRequestType = source.readInt();
mShortcutInfo = source.readParcelable(cl);
mShortcutInfo = mRequestType == REQUEST_TYPE_SHORTCUT ?
(ShortcutInfo) source.readParcelable(cl) : null;
mAppWidgetInfo = mRequestType == REQUEST_TYPE_APPWIDGET ?
(AppWidgetProviderInfo) source.readParcelable(cl) : null;
mInner = IPinItemRequest.Stub.asInterface(source.readStrongBinder());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mRequestType);
dest.writeParcelable(mShortcutInfo, flags);
if (mRequestType == REQUEST_TYPE_SHORTCUT) {
dest.writeParcelable(mShortcutInfo, flags);
}
if (mRequestType == REQUEST_TYPE_APPWIDGET) {
dest.writeParcelable(mAppWidgetInfo, flags);
}
dest.writeStrongBinder(mInner.asBinder());
}

View File

@@ -867,7 +867,7 @@ public class ShortcutManager {
*
* @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled.
* @throws IllegalStateException The caller doesn't have a foreground activity or a foreground
* service.
* service or when the user is locked.
*/
public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut,
@Nullable IntentSender resultIntent) {

View File

@@ -19,8 +19,10 @@ package android.content.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.os.ParcelFileDescriptor;
@@ -68,4 +70,8 @@ public abstract class ShortcutServiceInternal {
public abstract boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage);
public abstract boolean requestPinAppWidget(@NonNull String callingPackage,
@NonNull AppWidgetProviderInfo appWidget, @Nullable IntentSender resultIntent,
int userId);
}