Merge "Adding shared element transitions support in AppWidgets"

This commit is contained in:
Sunny Goyal
2018-11-01 16:19:12 +00:00
committed by Android (Google) Code Review
10 changed files with 428 additions and 257 deletions

View File

@@ -17,8 +17,12 @@
package android.appwidget;
import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
@@ -30,6 +34,7 @@ import android.os.CancellationSignal;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -44,6 +49,8 @@ import android.widget.RemoteViews.OnClickHandler;
import android.widget.RemoteViewsAdapter.RemoteAdapterConnectionCallback;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -651,4 +658,39 @@ public class AppWidgetHostView extends FrameLayout {
super.onInitializeAccessibilityNodeInfoInternal(info);
info.setClassName(AppWidgetHostView.class.getName());
}
/** @hide */
public ActivityOptions createSharedElementActivityOptions(
int[] sharedViewIds, String[] sharedViewNames, Intent fillInIntent) {
Context parentContext = getContext();
while ((parentContext instanceof ContextWrapper)
&& !(parentContext instanceof Activity)) {
parentContext = ((ContextWrapper) parentContext).getBaseContext();
}
if (!(parentContext instanceof Activity)) {
return null;
}
List<Pair<View, String>> sharedElements = new ArrayList<>();
Bundle extras = new Bundle();
for (int i = 0; i < sharedViewIds.length; i++) {
View view = findViewById(sharedViewIds[i]);
if (view != null) {
sharedElements.add(Pair.create(view, sharedViewNames[i]));
extras.putParcelable(sharedViewNames[i], RemoteViews.getSourceBounds(view));
}
}
if (!sharedElements.isEmpty()) {
fillInIntent.putExtra(RemoteViews.EXTRA_SHARED_ELEMENT_BOUNDS, extras);
final ActivityOptions opts = ActivityOptions.makeSceneTransitionAnimation(
(Activity) parentContext,
sharedElements.toArray(new Pair[sharedElements.size()]));
opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return opts;
}
return null;
}
}

View File

@@ -21,6 +21,7 @@ import android.annotation.DimenRes;
import android.annotation.NonNull;
import android.annotation.StyleRes;
import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
@@ -56,13 +57,14 @@ import android.os.StrictMode;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Filter;
import android.view.RemotableViewMethod;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.AdapterView.OnItemClickListener;
@@ -128,6 +130,12 @@ public class RemoteViews implements Parcelable, Filter {
*/
static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
/**
* The intent extra that contains the bounds for all shared elements.
*/
public static final String EXTRA_SHARED_ELEMENT_BOUNDS =
"android.widget.extra.SHARED_ELEMENT_BOUNDS";
/**
* Maximum depth of nested views calls from {@link #addView(int, RemoteViews)} and
* {@link #RemoteViews(RemoteViews, RemoteViews)}.
@@ -135,7 +143,7 @@ public class RemoteViews implements Parcelable, Filter {
private static final int MAX_NESTED_VIEWS = 10;
// The unique identifiers for each custom {@link Action}.
private static final int SET_ON_CLICK_PENDING_INTENT_TAG = 1;
private static final int SET_ON_CLICK_RESPONSE_TAG = 1;
private static final int REFLECTION_ACTION_TAG = 2;
private static final int SET_DRAWABLE_TINT_TAG = 3;
private static final int VIEW_GROUP_ACTION_ADD_TAG = 4;
@@ -143,7 +151,6 @@ public class RemoteViews implements Parcelable, Filter {
private static final int SET_EMPTY_VIEW_ACTION_TAG = 6;
private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7;
private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8;
private static final int SET_ON_CLICK_FILL_IN_INTENT_TAG = 9;
private static final int SET_REMOTE_VIEW_ADAPTER_INTENT_TAG = 10;
private static final int TEXT_VIEW_DRAWABLE_ACTION_TAG = 11;
private static final int BITMAP_REFLECTION_ACTION_TAG = 12;
@@ -228,7 +235,8 @@ public class RemoteViews implements Parcelable, Filter {
/** Class cookies of the Parcel this instance was read from. */
private final Map<Class, Object> mClassCookies;
private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = (view, pendingIntent, response)
-> startPendingIntent(view, pendingIntent, response.getLaunchOptions(view));
private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
@@ -362,57 +370,10 @@ public class RemoteViews implements Parcelable, Filter {
}
/** @hide */
public static class OnClickHandler {
@UnsupportedAppUsage
public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
try {
// TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Context context = view.getContext();
ActivityOptions opts = getActivityOptions(context);
// The NEW_TASK flags are applied through the activity options and not as a part of
// the call to startIntentSender() to ensure that they are consistently applied to
// both mutable and immutable PendingIntents.
context.startIntentSender(
pendingIntent.getIntentSender(), fillInIntent,
0, 0, 0, opts.toBundle());
} catch (IntentSender.SendIntentException e) {
android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
return false;
} catch (Exception e) {
android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
"unknown exception: ", e);
return false;
}
return true;
}
public interface OnClickHandler {
/** @hide */
protected ActivityOptions getActivityOptions(Context context) {
if (context.getResources().getBoolean(
com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
com.android.internal.R.styleable.Window);
int windowAnimations = windowStyle.getResourceId(
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
TypedArray windowAnimationStyle = context.obtainStyledAttributes(
windowAnimations, com.android.internal.R.styleable.WindowAnimation);
int enterAnimationId = windowAnimationStyle.getResourceId(com.android.internal.R
.styleable.WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0);
windowStyle.recycle();
windowAnimationStyle.recycle();
if (enterAnimationId != 0) {
final ActivityOptions opts = ActivityOptions.makeCustomAnimation(context,
enterAnimationId, 0);
opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return opts;
}
}
final ActivityOptions opts = ActivityOptions.makeBasic();
opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return opts;
}
boolean onClickHandler(View view, PendingIntent pendingIntent, RemoteResponse response);
}
/**
@@ -630,86 +591,6 @@ public class RemoteViews implements Parcelable, Filter {
}
}
private class SetOnClickFillInIntent extends Action {
public SetOnClickFillInIntent(int id, Intent fillInIntent) {
this.viewId = id;
this.fillInIntent = fillInIntent;
}
public SetOnClickFillInIntent(Parcel parcel) {
viewId = parcel.readInt();
fillInIntent = parcel.readTypedObject(Intent.CREATOR);
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(viewId);
dest.writeTypedObject(fillInIntent, 0 /* no flags */);
}
@Override
public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
if (!mIsWidgetCollectionChild) {
Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " +
"only from RemoteViewsFactory (ie. on collection items).");
return;
}
if (target == root) {
target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
} else if (fillInIntent != null) {
OnClickListener listener = new OnClickListener() {
public void onClick(View v) {
// Insure that this view is a child of an AdapterView
View parent = (View) v.getParent();
// Break the for loop on the first encounter of:
// 1) an AdapterView,
// 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
// 3) a null parent.
// 2) and 3) are unexpected and catch the case where a child is not
// correctly parented in an AdapterView.
while (parent != null && !(parent instanceof AdapterView<?>)
&& !((parent instanceof AppWidgetHostView) &&
!(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
parent = (View) parent.getParent();
}
if (!(parent instanceof AdapterView<?>)) {
// Somehow they've managed to get this far without having
// and AdapterView as a parent.
Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
return;
}
// Insure that a template pending intent has been set on an ancestor
if (!(parent.getTag() instanceof PendingIntent)) {
Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" +
" calling setPendingIntentTemplate on parent.");
return;
}
PendingIntent pendingIntent = (PendingIntent) parent.getTag();
final Rect rect = getSourceBounds(v);
fillInIntent.setSourceBounds(rect);
handler.onClickHandler(v, pendingIntent, fillInIntent);
}
};
target.setOnClickListener(listener);
}
}
@Override
public int getActionTag() {
return SET_ON_CLICK_FILL_IN_INTENT_TAG;
}
Intent fillInIntent;
}
private class SetPendingIntentTemplate extends Action {
public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
this.viewId = id;
@@ -749,22 +630,17 @@ public class RemoteViews implements Parcelable, Filter {
}
if (vg == null) return;
Intent fillInIntent = null;
RemoteResponse response = null;
int childCount = vg.getChildCount();
for (int i = 0; i < childCount; i++) {
Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
if (tag instanceof Intent) {
fillInIntent = (Intent) tag;
if (tag instanceof RemoteResponse) {
response = (RemoteResponse) tag;
break;
}
}
if (fillInIntent == null) return;
final Rect rect = getSourceBounds(view);
final Intent intent = new Intent();
intent.setSourceBounds(rect);
handler.onClickHandler(view, pendingIntentTemplate, fillInIntent);
if (response == null) return;
response.handleViewClick(view, handler);
}
}
};
@@ -922,20 +798,22 @@ public class RemoteViews implements Parcelable, Filter {
* {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
* to launch the provided {@link PendingIntent}.
*/
private class SetOnClickPendingIntent extends Action {
public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
private class SetOnClickResponse extends Action {
SetOnClickResponse(int id, RemoteResponse response) {
this.viewId = id;
this.pendingIntent = pendingIntent;
this.mResponse = response;
}
public SetOnClickPendingIntent(Parcel parcel) {
SetOnClickResponse(Parcel parcel) {
viewId = parcel.readInt();
pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
mResponse = new RemoteResponse();
mResponse.readFromParcel(parcel);
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(viewId);
PendingIntent.writePendingIntentOrNullToParcel(pendingIntent, dest);
mResponse.writeToParcel(dest, flags);
}
@Override
@@ -943,50 +821,54 @@ public class RemoteViews implements Parcelable, Filter {
final View target = root.findViewById(viewId);
if (target == null) return;
// If the view is an AdapterView, setting a PendingIntent on click doesn't make much
// sense, do they mean to set a PendingIntent template for the AdapterView's children?
if (mIsWidgetCollectionChild) {
Log.w(LOG_TAG, "Cannot setOnClickPendingIntent for collection item " +
"(id: " + viewId + ")");
ApplicationInfo appInfo = root.getContext().getApplicationInfo();
if (mResponse.mPendingIntent != null) {
// If the view is an AdapterView, setting a PendingIntent on click doesn't make
// much sense, do they mean to set a PendingIntent template for the
// AdapterView's children?
if (mIsWidgetCollectionChild) {
Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
+ "(id: " + viewId + ")");
ApplicationInfo appInfo = root.getContext().getApplicationInfo();
// We let this slide for HC and ICS so as to not break compatibility. It should have
// been disabled from the outset, but was left open by accident.
if (appInfo != null &&
appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
// We let this slide for HC and ICS so as to not break compatibility. It should
// have been disabled from the outset, but was left open by accident.
if (appInfo != null
&& appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
return;
}
}
target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
} else if (mResponse.mFillIntent != null) {
if (!mIsWidgetCollectionChild) {
Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
+ "only from RemoteViewsFactory (ie. on collection items).");
return;
}
if (target == root) {
// Target is a root node of an AdapterView child. Set the response in the tag.
// Actual click handling is done by OnItemClickListener in
// SetPendingIntentTemplate, which uses this tag information.
target.setTagInternal(com.android.internal.R.id.fillInIntent, mResponse);
return;
}
} else {
// No intent to apply
target.setOnClickListener(null);
return;
}
// If the pendingIntent is null, we clear the onClickListener
OnClickListener listener = null;
if (pendingIntent != null) {
listener = new OnClickListener() {
public void onClick(View v) {
// Find target view location in screen coordinates and
// fill into PendingIntent before sending.
final Rect rect = getSourceBounds(v);
final Intent intent = new Intent();
intent.setSourceBounds(rect);
handler.onClickHandler(v, pendingIntent, intent);
}
};
}
target.setTagInternal(R.id.pending_intent_tag, pendingIntent);
target.setOnClickListener(listener);
target.setOnClickListener(v -> mResponse.handleViewClick(v, handler));
}
@Override
public int getActionTag() {
return SET_ON_CLICK_PENDING_INTENT_TAG;
return SET_ON_CLICK_RESPONSE_TAG;
}
@UnsupportedAppUsage
PendingIntent pendingIntent;
final RemoteResponse mResponse;
}
private static Rect getSourceBounds(View v) {
/** @hide **/
public static Rect getSourceBounds(View v) {
final float appScale = v.getContext().getResources()
.getCompatibilityInfo().applicationScale;
final int[] pos = new int[2];
@@ -2413,8 +2295,8 @@ public class RemoteViews implements Parcelable, Filter {
private Action getActionFromParcel(Parcel parcel, int depth) {
int tag = parcel.readInt();
switch (tag) {
case SET_ON_CLICK_PENDING_INTENT_TAG:
return new SetOnClickPendingIntent(parcel);
case SET_ON_CLICK_RESPONSE_TAG:
return new SetOnClickResponse(parcel);
case SET_DRAWABLE_TINT_TAG:
return new SetDrawableTint(parcel);
case REFLECTION_ACTION_TAG:
@@ -2430,8 +2312,6 @@ public class RemoteViews implements Parcelable, Filter {
return new SetEmptyView(parcel);
case SET_PENDING_INTENT_TEMPLATE_TAG:
return new SetPendingIntentTemplate(parcel);
case SET_ON_CLICK_FILL_IN_INTENT_TAG:
return new SetOnClickFillInIntent(parcel);
case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG:
return new SetRemoteViewsAdapterIntent(parcel);
case TEXT_VIEW_DRAWABLE_ACTION_TAG:
@@ -2834,7 +2714,7 @@ public class RemoteViews implements Parcelable, Filter {
* to launch the provided {@link PendingIntent}. The source bounds
* ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the clicked
* view in screen space.
* Note that any activity options associated with the pendingIntent may get overridden
* Note that any activity options associated with the mPendingIntent may get overridden
* before starting the intent.
*
* When setting the on-click action of items within collections (eg. {@link ListView},
@@ -2846,7 +2726,19 @@ public class RemoteViews implements Parcelable, Filter {
* @param pendingIntent The {@link PendingIntent} to send when user clicks
*/
public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
setOnClickResponse(viewId, RemoteResponse.fromPendingIntent(pendingIntent));
}
/**
* Equivalent of calling
* {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
* to launch the provided {@link RemoteResponse}.
*
* @param viewId The id of the view that will trigger the {@link RemoteResponse} when clicked
* @param response The {@link RemoteResponse} to send when user clicks
*/
public void setOnClickResponse(int viewId, RemoteResponse response) {
addAction(new SetOnClickResponse(viewId, response));
}
/**
@@ -2883,7 +2775,7 @@ public class RemoteViews implements Parcelable, Filter {
* in order to determine the on-click behavior of the view specified by viewId
*/
public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
setOnClickResponse(viewId, RemoteResponse.fromFillInIntent(fillInIntent));
}
/**
@@ -3903,4 +3795,213 @@ public class RemoteViews implements Parcelable, Filter {
}
}
}
/**
* Class representing a response to an action performed on any element of a RemoteViews.
*/
public static class RemoteResponse {
private PendingIntent mPendingIntent;
private Intent mFillIntent;
private IntArray mViewIds;
private ArrayList<String> mElementNames;
/**
* Creates a response which sends a pending intent as part of the response. The source
* bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
* target view in screen space.
* Note that any activity options associated with the mPendingIntent may get overridden
* before starting the intent.
*
* @param pendingIntent The {@link PendingIntent} to send as part of the response
*/
public static RemoteResponse fromPendingIntent(PendingIntent pendingIntent) {
RemoteResponse response = new RemoteResponse();
response.mPendingIntent = pendingIntent;
return response;
}
/**
* When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is
* very costly to set PendingIntents on the individual items, and is hence not permitted.
* Instead a single PendingIntent template can be set on the collection, see {@link
* RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
* action of a given item can be distinguished by setting a fillInIntent on that item. The
* fillInIntent is then combined with the PendingIntent template in order to determine the
* final intent which will be executed when the item is clicked. This works as follows: any
* fields which are left blank in the PendingIntent template, but are provided by the
* fillInIntent will be overwritten, and the resulting PendingIntent will be used. The rest
* of the PendingIntent template will then be filled in with the associated fields that are
* set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
* Creates a response which sends a pending intent as part of the response. The source
* bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
* target view in screen space.
* Note that any activity options associated with the mPendingIntent may get overridden
* before starting the intent.
*
* @param fillIntent The intent which will be combined with the parent's PendingIntent in
* order to determine the behavior of the response
*
* @see RemoteViews#setPendingIntentTemplate(int, PendingIntent)
* @see RemoteViews#setOnClickFillInIntent(int, Intent)
* @return
*/
public static RemoteResponse fromFillInIntent(Intent fillIntent) {
RemoteResponse response = new RemoteResponse();
response.mFillIntent = fillIntent;
return response;
}
/**
* Adds a shared element to be transferred as part of the transition between Activities
* using cross-Activity scene animations. The position of the first element will be used as
* the epicenter for the exit Transition. The position of the associated shared element in
* the launched Activity will be the epicenter of its entering Transition.
*
* @param viewId The id of the view to be shared as part of the transition
* @param sharedElementName The shared element name for this view
*
* @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])
*/
public RemoteResponse addSharedElement(int viewId, String sharedElementName) {
if (mViewIds == null) {
mViewIds = new IntArray();
mElementNames = new ArrayList<>();
}
mViewIds.add(viewId);
mElementNames.add(sharedElementName);
return this;
}
private void writeToParcel(Parcel dest, int flags) {
PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
if (mPendingIntent == null) {
// Only write the intent if pending intent is null
dest.writeTypedObject(mFillIntent, flags);
}
dest.writeIntArray(mViewIds == null ? null : mViewIds.toArray());
dest.writeStringList(mElementNames);
}
private void readFromParcel(Parcel parcel) {
mPendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
if (mPendingIntent == null) {
mFillIntent = parcel.readTypedObject(Intent.CREATOR);
}
int[] viewIds = parcel.createIntArray();
mViewIds = viewIds == null ? null : IntArray.wrap(viewIds);
mElementNames = parcel.createStringArrayList();
}
private void handleViewClick(View v, OnClickHandler handler) {
final PendingIntent pi;
if (mPendingIntent != null) {
pi = mPendingIntent;
} else if (mFillIntent != null) {
// Insure that this view is a child of an AdapterView
View parent = (View) v.getParent();
// Break the for loop on the first encounter of:
// 1) an AdapterView,
// 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
// 3) a null parent.
// 2) and 3) are unexpected and catch the case where a child is not
// correctly parented in an AdapterView.
while (parent != null && !(parent instanceof AdapterView<?>)
&& !((parent instanceof AppWidgetHostView)
&& !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
parent = (View) parent.getParent();
}
if (!(parent instanceof AdapterView<?>)) {
// Somehow they've managed to get this far without having
// and AdapterView as a parent.
Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
return;
}
// Insure that a template pending intent has been set on an ancestor
if (!(parent.getTag() instanceof PendingIntent)) {
Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without"
+ " calling setPendingIntentTemplate on parent.");
return;
}
pi = (PendingIntent) parent.getTag();
} else {
Log.e(LOG_TAG, "Response has neither pendingIntent nor fillInIntent");
return;
}
handler.onClickHandler(v, pi, this);
}
/** @hide */
public Pair<Intent, ActivityOptions> getLaunchOptions(View view) {
Intent intent = mPendingIntent != null ? new Intent() : new Intent(mFillIntent);
intent.setSourceBounds(getSourceBounds(view));
ActivityOptions opts = null;
Context context = view.getContext();
if (context.getResources().getBoolean(
com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
com.android.internal.R.styleable.Window);
int windowAnimations = windowStyle.getResourceId(
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
TypedArray windowAnimationStyle = context.obtainStyledAttributes(
windowAnimations, com.android.internal.R.styleable.WindowAnimation);
int enterAnimationId = windowAnimationStyle.getResourceId(com.android.internal.R
.styleable.WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0);
windowStyle.recycle();
windowAnimationStyle.recycle();
if (enterAnimationId != 0) {
opts = ActivityOptions.makeCustomAnimation(context,
enterAnimationId, 0);
opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
}
if (opts == null && mViewIds != null && mElementNames != null) {
View parent = (View) view.getParent();
while (parent != null && !(parent instanceof AppWidgetHostView)) {
parent = (View) parent.getParent();
}
if (parent instanceof AppWidgetHostView) {
opts = ((AppWidgetHostView) parent).createSharedElementActivityOptions(
mViewIds.toArray(),
mElementNames.toArray(new String[mElementNames.size()]), intent);
}
}
if (opts == null) {
opts = ActivityOptions.makeBasic();
opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
return Pair.create(intent, opts);
}
}
/** @hide */
public static boolean startPendingIntent(View view, PendingIntent pendingIntent,
Pair<Intent, ActivityOptions> options) {
try {
// TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Context context = view.getContext();
// The NEW_TASK flags are applied through the activity options and not as a part of
// the call to startIntentSender() to ensure that they are consistently applied to
// both mutable and immutable PendingIntents.
context.startIntentSender(
pendingIntent.getIntentSender(), options.first,
0, 0, 0, options.second.toBundle());
} catch (IntentSender.SendIntentException e) {
Log.e(LOG_TAG, "Cannot send pending intent: ", e);
return false;
} catch (Exception e) {
Log.e(LOG_TAG, "Cannot send pending intent due to unknown exception: ", e);
return false;
}
return true;
}
}