• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.server.wm.app;
18 
19 import static android.server.wm.app.Components.PipActivity.ACTION_CHANGE_ASPECT_RATIO;
20 import static android.server.wm.app.Components.PipActivity.ACTION_ENTER_PIP;
21 import static android.server.wm.app.Components.PipActivity.ACTION_ENTER_PIP_AND_WAIT_FOR_UI_STATE;
22 import static android.server.wm.app.Components.PipActivity.ACTION_EXPAND_PIP;
23 import static android.server.wm.app.Components.PipActivity.ACTION_FINISH;
24 import static android.server.wm.app.Components.PipActivity.ACTION_LAUNCH_TRANSLUCENT_ACTIVITY;
25 import static android.server.wm.app.Components.PipActivity.ACTION_MOVE_TO_BACK;
26 import static android.server.wm.app.Components.PipActivity.ACTION_ON_PIP_REQUESTED;
27 import static android.server.wm.app.Components.PipActivity.ACTION_SET_ON_PAUSE_REMOTE_CALLBACK;
28 import static android.server.wm.app.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
29 import static android.server.wm.app.Components.PipActivity.ACTION_UPDATE_PIP_STATE;
30 import static android.server.wm.app.Components.PipActivity.EXTRA_ALLOW_AUTO_PIP;
31 import static android.server.wm.app.Components.PipActivity.EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP;
32 import static android.server.wm.app.Components.PipActivity.EXTRA_CLOSE_ACTION;
33 import static android.server.wm.app.Components.PipActivity.EXTRA_DISMISS_KEYGUARD;
34 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
35 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR;
36 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR;
37 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP_ON_BACK_PRESSED;
38 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP_ON_PAUSE;
39 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP_ON_PIP_REQUESTED;
40 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP_ON_USER_LEAVE_HINT;
41 import static android.server.wm.app.Components.PipActivity.EXTRA_EXPANDED_PIP_ASPECT_RATIO_DENOMINATOR;
42 import static android.server.wm.app.Components.PipActivity.EXTRA_EXPANDED_PIP_ASPECT_RATIO_NUMERATOR;
43 import static android.server.wm.app.Components.PipActivity.EXTRA_FINISH_SELF_ON_RESUME;
44 import static android.server.wm.app.Components.PipActivity.EXTRA_IS_SEAMLESS_RESIZE_ENABLED;
45 import static android.server.wm.app.Components.PipActivity.EXTRA_NUMBER_OF_CUSTOM_ACTIONS;
46 import static android.server.wm.app.Components.PipActivity.EXTRA_ON_PAUSE_DELAY;
47 import static android.server.wm.app.Components.PipActivity.EXTRA_PIP_ON_PAUSE_CALLBACK;
48 import static android.server.wm.app.Components.PipActivity.EXTRA_PIP_ORIENTATION;
49 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_DENOMINATOR;
50 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_NUMERATOR;
51 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR;
52 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR;
53 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_PIP_CALLBACK;
54 import static android.server.wm.app.Components.PipActivity.EXTRA_SET_PIP_STASHED;
55 import static android.server.wm.app.Components.PipActivity.EXTRA_SHOW_OVER_KEYGUARD;
56 import static android.server.wm.app.Components.PipActivity.EXTRA_START_ACTIVITY;
57 import static android.server.wm.app.Components.PipActivity.EXTRA_SUBTITLE;
58 import static android.server.wm.app.Components.PipActivity.EXTRA_TAP_TO_FINISH;
59 import static android.server.wm.app.Components.PipActivity.EXTRA_TITLE;
60 import static android.server.wm.app.Components.PipActivity.IS_IN_PIP_MODE_RESULT;
61 import static android.server.wm.app.Components.PipActivity.UI_STATE_ENTERING_PIP_RESULT;
62 import static android.server.wm.app.Components.PipActivity.UI_STATE_STASHED_RESULT;
63 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
64 
65 import android.app.Activity;
66 import android.app.PendingIntent;
67 import android.app.PictureInPictureParams;
68 import android.app.PictureInPictureUiState;
69 import android.app.RemoteAction;
70 import android.content.BroadcastReceiver;
71 import android.content.ComponentName;
72 import android.content.Context;
73 import android.content.Intent;
74 import android.content.IntentFilter;
75 import android.content.res.Configuration;
76 import android.graphics.drawable.Icon;
77 import android.os.Bundle;
78 import android.os.Handler;
79 import android.os.RemoteCallback;
80 import android.os.SystemClock;
81 import android.server.wm.CommandSession;
82 import android.util.Log;
83 import android.util.Rational;
84 import android.window.OnBackInvokedDispatcher;
85 
86 import androidx.annotation.Nullable;
87 
88 import java.util.ArrayList;
89 import java.util.List;
90 
91 public class PipActivity extends AbstractLifecycleLogActivity {
92 
93     private boolean mEnteredPictureInPicture;
94     private boolean mEnterPipOnBackPressed;
95     private RemoteCallback mCb;
96     private RemoteCallback mOnPauseCallback;
97 
98     private Handler mHandler = new Handler();
99     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
100         @Override
101         public void onReceive(Context context, Intent intent) {
102             if (intent != null) {
103                 switch (intent.getAction()) {
104                     case ACTION_ENTER_PIP:
105                         enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
106                         if (intent.getExtras() != null) {
107                             mCb = (RemoteCallback) intent.getExtras().get(EXTRA_SET_PIP_CALLBACK);
108                             if (mCb != null) {
109                                 mCb.sendResult(new Bundle());
110                             }
111                         }
112                         break;
113                     case ACTION_ENTER_PIP_AND_WAIT_FOR_UI_STATE:
114                         // mCb will be callback-ed in onPictureInPictureUiStateChanged.
115                         mCb = (RemoteCallback) intent.getExtras().get(EXTRA_SET_PIP_CALLBACK);
116                         enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
117                         break;
118                     case ACTION_MOVE_TO_BACK:
119                         moveTaskToBack(false /* nonRoot */);
120                         break;
121                     case ACTION_UPDATE_PIP_STATE:
122                         mCb = (RemoteCallback) intent.getExtras().get(EXTRA_SET_PIP_CALLBACK);
123                         boolean stashed = intent.getBooleanExtra(EXTRA_SET_PIP_STASHED, false);
124                         onPictureInPictureUiStateChanged(new PictureInPictureUiState(stashed));
125                         break;
126                     case ACTION_EXPAND_PIP:
127                         // Trigger the activity to expand
128                         Intent startIntent = new Intent(PipActivity.this, PipActivity.class);
129                         startIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
130                         startActivity(startIntent);
131 
132                         if (intent.hasExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR)
133                                 && intent.hasExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR)) {
134                             // Ugly, but required to wait for the startActivity to actually start
135                             // the activity...
136                             mHandler.postDelayed(() -> {
137                                 final PictureInPictureParams.Builder builder =
138                                         new PictureInPictureParams.Builder();
139                                 builder.setAspectRatio(getAspectRatio(intent,
140                                         EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR,
141                                         EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR));
142                                 setPictureInPictureParams(builder.build());
143                             }, 100);
144                         }
145                         break;
146                     case ACTION_SET_REQUESTED_ORIENTATION:
147                         setRequestedOrientation(Integer.parseInt(intent.getStringExtra(
148                                 EXTRA_PIP_ORIENTATION)));
149                         break;
150                     case ACTION_FINISH:
151                         finish();
152                         break;
153                     case ACTION_ON_PIP_REQUESTED:
154                         onPictureInPictureRequested();
155                         break;
156                     case ACTION_CHANGE_ASPECT_RATIO:
157                         setPictureInPictureParams(new PictureInPictureParams.Builder()
158                                 .setAspectRatio(getAspectRatio(intent,
159                                         EXTRA_SET_ASPECT_RATIO_NUMERATOR,
160                                         EXTRA_SET_ASPECT_RATIO_DENOMINATOR))
161                                 .build());
162                         break;
163                     case ACTION_LAUNCH_TRANSLUCENT_ACTIVITY:
164                         startActivity(new Intent(PipActivity.this, TranslucentTestActivity.class));
165                         break;
166                     case ACTION_SET_ON_PAUSE_REMOTE_CALLBACK:
167                         mOnPauseCallback = intent.getParcelableExtra(
168                                 EXTRA_PIP_ON_PAUSE_CALLBACK, RemoteCallback.class);
169                         // Signals the caller that we have received the mOnPauseCallback
170                         final RemoteCallback setCallback = intent.getParcelableExtra(
171                                 EXTRA_SET_PIP_CALLBACK, RemoteCallback.class);
172                         setCallback.sendResult(Bundle.EMPTY);
173                         break;
174                 }
175             }
176         }
177     };
178 
179     @Override
onCreate(Bundle savedInstanceState)180     protected void onCreate(Bundle savedInstanceState) {
181         super.onCreate(savedInstanceState);
182 
183         // Set the fixed orientation if requested
184         if (getIntent().hasExtra(EXTRA_PIP_ORIENTATION)) {
185             final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_PIP_ORIENTATION));
186             setRequestedOrientation(ori);
187         }
188 
189         // Set the window flag to show over the keyguard
190         setShowWhenLocked(parseBooleanExtra(EXTRA_SHOW_OVER_KEYGUARD));
191 
192         // Set the window flag to dismiss the keyguard
193         if (parseBooleanExtra(EXTRA_DISMISS_KEYGUARD)) {
194             getWindow().addFlags(FLAG_DISMISS_KEYGUARD);
195         }
196 
197         boolean enteringPip = false;
198         // Enter picture in picture with the given aspect ratio if provided
199         if (parseBooleanExtra(EXTRA_ENTER_PIP)) {
200             if (getIntent().hasExtra(EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR)
201                     && getIntent().hasExtra(EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR)) {
202                 try {
203                     final PictureInPictureParams.Builder builder =
204                             new PictureInPictureParams.Builder();
205                     builder.setAspectRatio(getAspectRatio(getIntent(),
206                             EXTRA_ENTER_PIP_ASPECT_RATIO_NUMERATOR,
207                             EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR));
208                     if (shouldAddExpandedPipAspectRatios()) {
209                         builder.setExpandedAspectRatio(getAspectRatio(getIntent(),
210                                 EXTRA_EXPANDED_PIP_ASPECT_RATIO_NUMERATOR,
211                                 EXTRA_EXPANDED_PIP_ASPECT_RATIO_DENOMINATOR));
212                     }
213                     enteringPip = enterPictureInPictureMode(builder.build());
214                 } catch (Exception e) {
215                     // This call can fail intentionally if the aspect ratio is too extreme
216                 }
217             } else {
218                 enteringPip = enterPictureInPictureMode(
219                         new PictureInPictureParams.Builder().build());
220             }
221         }
222 
223         // We need to wait for either enterPictureInPicture() or requestAutoEnterPictureInPicture()
224         // to be called before setting the aspect ratio
225         if (getIntent().hasExtra(EXTRA_SET_ASPECT_RATIO_NUMERATOR)
226                 && getIntent().hasExtra(EXTRA_SET_ASPECT_RATIO_DENOMINATOR)) {
227             final PictureInPictureParams.Builder builder =
228                     new PictureInPictureParams.Builder();
229             builder.setAspectRatio(getAspectRatio(getIntent(),
230                     EXTRA_SET_ASPECT_RATIO_NUMERATOR, EXTRA_SET_ASPECT_RATIO_DENOMINATOR));
231             try {
232                 setPictureInPictureParams(builder.build());
233             } catch (Exception e) {
234                 // This call can fail intentionally if the aspect ratio is too extreme
235             }
236         }
237 
238         final PictureInPictureParams.Builder sharedBuilder = new PictureInPictureParams.Builder();
239         boolean sharedBuilderChanged = false;
240 
241         if (parseBooleanExtra(EXTRA_ALLOW_AUTO_PIP)) {
242             sharedBuilder.setAutoEnterEnabled(true);
243             sharedBuilderChanged = true;
244         }
245 
246         if (getIntent().hasExtra(EXTRA_IS_SEAMLESS_RESIZE_ENABLED)) {
247             sharedBuilder.setSeamlessResizeEnabled(
248                     getIntent().getBooleanExtra(EXTRA_IS_SEAMLESS_RESIZE_ENABLED, true));
249             sharedBuilderChanged = true;
250         }
251 
252         if (getIntent().hasExtra(EXTRA_TITLE)) {
253             sharedBuilder.setTitle(getIntent().getStringExtra(EXTRA_TITLE));
254             sharedBuilderChanged = true;
255         }
256 
257         if (getIntent().hasExtra(EXTRA_SUBTITLE)) {
258             sharedBuilder.setSubtitle(getIntent().getStringExtra(EXTRA_SUBTITLE));
259             sharedBuilderChanged = true;
260         }
261 
262         if (getIntent().hasExtra(EXTRA_CLOSE_ACTION)) {
263             if (getIntent().getBooleanExtra(EXTRA_CLOSE_ACTION, false)) {
264                 sharedBuilder.setCloseAction(createRemoteAction(0));
265             } else {
266                 sharedBuilder.setCloseAction(null);
267             }
268             sharedBuilderChanged = true;
269         }
270 
271         // Enable tap to finish if necessary
272         if (parseBooleanExtra(EXTRA_TAP_TO_FINISH)) {
273             setContentView(R.layout.tap_to_finish_pip_layout);
274             findViewById(R.id.content).setOnClickListener(v -> {
275                 finish();
276             });
277         }
278 
279         // Launch a new activity if requested
280         String launchActivityComponent = getIntent().getStringExtra(EXTRA_START_ACTIVITY);
281         if (launchActivityComponent != null) {
282             Intent launchIntent = new Intent();
283             launchIntent.setComponent(ComponentName.unflattenFromString(launchActivityComponent));
284             startActivity(launchIntent);
285         }
286 
287         // Set custom actions if requested
288         if (getIntent().hasExtra(EXTRA_NUMBER_OF_CUSTOM_ACTIONS)) {
289             final int numberOfCustomActions = Integer.valueOf(
290                     getIntent().getStringExtra(EXTRA_NUMBER_OF_CUSTOM_ACTIONS));
291             final List<RemoteAction> actions = new ArrayList<>(numberOfCustomActions);
292             for (int i = 0; i< numberOfCustomActions; i++) {
293                 actions.add(createRemoteAction(i));
294             }
295             sharedBuilder.setActions(actions);
296             sharedBuilderChanged = true;
297         }
298 
299         if (sharedBuilderChanged) {
300             setPictureInPictureParams(sharedBuilder.build());
301         }
302 
303         // Register the broadcast receiver
304         IntentFilter filter = new IntentFilter();
305         filter.addAction(ACTION_ENTER_PIP);
306         filter.addAction(ACTION_MOVE_TO_BACK);
307         filter.addAction(ACTION_EXPAND_PIP);
308         filter.addAction(ACTION_UPDATE_PIP_STATE);
309         filter.addAction(ACTION_ENTER_PIP_AND_WAIT_FOR_UI_STATE);
310         filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
311         filter.addAction(ACTION_FINISH);
312         filter.addAction(ACTION_ON_PIP_REQUESTED);
313         filter.addAction(ACTION_CHANGE_ASPECT_RATIO);
314         filter.addAction(ACTION_LAUNCH_TRANSLUCENT_ACTIVITY);
315         filter.addAction(ACTION_SET_ON_PAUSE_REMOTE_CALLBACK);
316         registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
317 
318         // Don't dump configuration when entering PIP to avoid the verifier getting the intermediate
319         // state. In this case it is expected that the verifier will check the changed configuration
320         // after onConfigurationChanged.
321         if (!enteringPip) {
322             // Dump applied display metrics.
323             dumpConfiguration(getResources().getConfiguration());
324             dumpConfigInfo();
325         }
326 
327         mEnterPipOnBackPressed = parseBooleanExtra(EXTRA_ENTER_PIP_ON_BACK_PRESSED);
328         getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
329                 OnBackInvokedDispatcher.PRIORITY_DEFAULT, this::onBackInvoked);
330     }
331 
332     @Override
onResume()333     protected void onResume() {
334         super.onResume();
335 
336         // Finish self if requested
337         if (parseBooleanExtra(EXTRA_FINISH_SELF_ON_RESUME)) {
338             finish();
339         }
340     }
341 
342     @Override
onPause()343     protected void onPause() {
344         super.onPause();
345 
346         // Pause if requested
347         if (getIntent().hasExtra(EXTRA_ON_PAUSE_DELAY)) {
348             SystemClock.sleep(Long.valueOf(getIntent().getStringExtra(EXTRA_ON_PAUSE_DELAY)));
349         }
350 
351         // Enter PIP on move to background
352         if (parseBooleanExtra(EXTRA_ENTER_PIP_ON_PAUSE)) {
353             enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
354         }
355 
356         if (mOnPauseCallback != null) {
357             Bundle res = new Bundle(1);
358             res.putBoolean(IS_IN_PIP_MODE_RESULT, isInPictureInPictureMode());
359             mOnPauseCallback.sendResult(res);
360         }
361     }
362 
363     @Override
onStop()364     protected void onStop() {
365         super.onStop();
366 
367         if (parseBooleanExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP) && !mEnteredPictureInPicture) {
368             Log.w(getTag(), "Unexpected onStop() called before entering picture-in-picture");
369             finish();
370         }
371     }
372 
373     @Override
onDestroy()374     protected void onDestroy() {
375         super.onDestroy();
376 
377         unregisterReceiver(mReceiver);
378     }
379 
380     @Override
onUserLeaveHint()381     protected void onUserLeaveHint() {
382         super.onUserLeaveHint();
383         if (parseBooleanExtra(EXTRA_ENTER_PIP_ON_USER_LEAVE_HINT)) {
384             enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
385         }
386     }
387 
388     @Override
onPictureInPictureRequested()389     public boolean onPictureInPictureRequested() {
390         onCallback(CommandSession.ActivityCallback.ON_PICTURE_IN_PICTURE_REQUESTED);
391         if (parseBooleanExtra(EXTRA_ENTER_PIP_ON_PIP_REQUESTED)) {
392             enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
393             return true;
394         }
395         return super.onPictureInPictureRequested();
396     }
397 
398     @Override
onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig)399     public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode,
400             Configuration newConfig) {
401         super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
402 
403         // Fail early if the activity state does not match the dispatched state
404         if (isInPictureInPictureMode() != isInPictureInPictureMode) {
405             Log.w(getTag(), "Received onPictureInPictureModeChanged mode="
406                     + isInPictureInPictureMode + " activityState=" + isInPictureInPictureMode());
407             finish();
408         }
409 
410         // Mark that we've entered picture-in-picture so that we can stop checking for
411         // EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP
412         if (isInPictureInPictureMode) {
413             mEnteredPictureInPicture = true;
414         }
415     }
416 
417     @Override
onPictureInPictureUiStateChanged(PictureInPictureUiState pipState)418     public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) {
419         Bundle res = new Bundle();
420         res.putBoolean(UI_STATE_STASHED_RESULT, pipState.isStashed());
421         res.putBoolean(UI_STATE_ENTERING_PIP_RESULT, pipState.isTransitioningToPip());
422         if (mCb != null) {
423             mCb.sendResult(res);
424         }
425     }
426 
427     @Override
onConfigurationChanged(Configuration newConfig)428     public void onConfigurationChanged(Configuration newConfig) {
429         super.onConfigurationChanged(newConfig);
430         dumpConfiguration(newConfig);
431         dumpConfigInfo();
432     }
433 
onBackInvoked()434     private void onBackInvoked() {
435         if (mEnterPipOnBackPressed) {
436             enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
437         } else {
438             finish();
439         }
440     }
441 
442     /**
443      * Launches a new instance of the PipActivity in the same task that will automatically enter
444      * PiP.
445      */
launchEnterPipActivity(Activity caller, @Nullable Bundle overrides)446     static void launchEnterPipActivity(Activity caller, @Nullable Bundle overrides) {
447         final Intent intent = new Intent(caller, PipActivity.class);
448         intent.putExtra(EXTRA_ENTER_PIP, "true");
449         intent.putExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP, "true");
450         if (overrides != null) {
451             intent.putExtras(overrides);
452         }
453         caller.startActivity(intent);
454     }
455 
parseBooleanExtra(String key)456     private boolean parseBooleanExtra(String key) {
457         return getIntent().hasExtra(key) && Boolean.parseBoolean(getIntent().getStringExtra(key));
458     }
459 
460     /**
461      * @return a {@link Rational} aspect ratio from the given intent and extras.
462      */
getAspectRatio(Intent intent, String extraNum, String extraDenom)463     private Rational getAspectRatio(Intent intent, String extraNum, String extraDenom) {
464         return new Rational(
465                 Integer.valueOf(intent.getStringExtra(extraNum)),
466                 Integer.valueOf(intent.getStringExtra(extraDenom)));
467     }
468 
469     /** @return {@link RemoteAction} instance titled after a given index */
createRemoteAction(int index)470     private RemoteAction createRemoteAction(int index) {
471         return new RemoteAction(Icon.createWithResource(this, R.drawable.red),
472                 "action " + index,
473                 "contentDescription " + index,
474                 PendingIntent.getBroadcast(this, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE));
475     }
476 
shouldAddExpandedPipAspectRatios()477     private boolean shouldAddExpandedPipAspectRatios() {
478         return getIntent().hasExtra(EXTRA_EXPANDED_PIP_ASPECT_RATIO_NUMERATOR)
479             && getIntent().hasExtra(EXTRA_EXPANDED_PIP_ASPECT_RATIO_DENOMINATOR);
480     }
481 }
482