• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 com.android.managedprovisioning.common;
18 
19 import static com.android.managedprovisioning.provisioning.Constants.ENABLE_CUSTOM_TRANSITIONS;
20 
21 import static com.google.android.setupdesign.transition.TransitionHelper.EXTRA_ACTIVITY_OPTIONS;
22 import static com.google.android.setupdesign.transition.TransitionHelper.TRANSITION_FADE;
23 import static com.google.android.setupdesign.transition.TransitionHelper.makeActivityOptions;
24 
25 import static java.util.Objects.requireNonNull;
26 
27 import android.app.Activity;
28 import android.content.Intent;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.Looper;
32 import android.os.UserHandle;
33 import android.view.ViewGroup;
34 import android.view.Window;
35 
36 import androidx.annotation.Nullable;
37 
38 /**
39  * Wrapper for {@link com.google.android.setupdesign.transition.TransitionHelper}.
40  *
41  * <p>Each {@link Activity#onCreate(Bundle)} is expected to call {@link
42  * TransitionHelper#applyContentScreenTransitions(Activity)}. For starting an {@link Activity}, use
43  * any of the {@code startActivity*WithTransition} methods provided in this class, in order to
44  * apply the appropriate transition. When finishing an {@link Activity}, {@link
45  * TransitionHelper#finishActivity(Activity)} must be used to apply the relevant transition.
46  *
47  * @see #startActivityWithTransition(Activity, Intent)
48  * @see #startActivityForResultWithTransition(Activity, Intent, int)
49  * @see #startActivityAsUserWithTransition(Activity, Intent, UserHandle)
50  * @see #startActivityForResultAsUserWithTransition(Activity, Intent, int, UserHandle)
51  */
52 // TODO(b/183631247): Add tests once infrastructure is fixed
53 public class TransitionHelper {
54     private final Handler mHandler = new Handler(Looper.myLooper());
55 
56     /**
57      * Applies the relevant {@link Activity} transitions for this content screen.
58      * Must be called in {@link Activity#onCreate(Bundle)}.
59      *
60      * <p>A content screen is a screen that shows content, not a spinner.
61      *
62      * <p>Also enables the {@link Window#FEATURE_ACTIVITY_TRANSITIONS} feature so that transitions
63      * are supported.
64      *
65      * @see #applyWaitingScreenTransitions(Activity)
66      */
applyContentScreenTransitions(Activity activity)67     public void applyContentScreenTransitions(Activity activity) {
68         requireNonNull(activity);
69         if (!ENABLE_CUSTOM_TRANSITIONS) {
70             return;
71         }
72         activity.getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
73         com.google.android.setupdesign.transition.TransitionHelper.applyForwardTransition(activity);
74         com.google.android.setupdesign.transition.TransitionHelper
75                 .applyBackwardTransition(activity);
76     }
77 
78     /**
79      * Applies the relevant {@link Activity} transitions for this waiting screen.
80      * Must be called in {@link Activity#onResume()}.
81      *
82      * <p>A waiting screen is a screen that shows a spinner, not content.
83      *
84      * @see #applyContentScreenTransitions(Activity)
85      */
applyWaitingScreenTransitions(Activity activity)86     public void applyWaitingScreenTransitions(Activity activity) {
87         requireNonNull(activity);
88         com.google.android.setupdesign.transition.TransitionHelper
89                 .applyForwardTransition(activity, TRANSITION_FADE);
90     }
91 
92     /**
93      * Calls {@link Activity#startActivity(Intent)} with an appropriate transition.
94      */
startActivityWithTransition(Activity activity, Intent intent)95     public void startActivityWithTransition(Activity activity, Intent intent) {
96         requireNonNull(activity);
97         requireNonNull(intent);
98         if (!ENABLE_CUSTOM_TRANSITIONS) {
99             activity.startActivity(intent);
100             return;
101         }
102         // TODO(b/183537077): Remove hack to start UI-less activities using the previous activity's
103         //  options bundle
104         Bundle previousBundle = getActivityOptionsForActivity(activity);
105         if (!hasContentView(activity) && previousBundle != null) {
106             mHandler.post(() -> com.google.android.setupdesign.transition.TransitionHelper
107                     .startActivityWithTransition(activity, intent, previousBundle));
108         } else {
109             mHandler.post(() -> com.google.android.setupdesign.transition.TransitionHelper
110                     .startActivityWithTransition(activity, intent));
111         }
112     }
113 
114     /**
115      * Calls {@link Activity#startActivityForResult(Intent, int)} with an appropriate transition.
116      */
startActivityForResultWithTransition( Activity activity, Intent intent, int requestCode)117     public void startActivityForResultWithTransition(
118             Activity activity, Intent intent, int requestCode) {
119         requireNonNull(activity);
120         requireNonNull(intent);
121         if (!ENABLE_CUSTOM_TRANSITIONS) {
122             activity.startActivityForResult(intent, requestCode);
123             return;
124         }
125         // TODO(b/183537077): Remove hack to start UI-less activities using the previous activity's
126         //  options bundle
127         Bundle previousBundle = getActivityOptionsForActivity(activity);
128         if (!hasContentView(activity) && previousBundle != null) {
129             mHandler.post(() -> com.google.android.setupdesign.transition.TransitionHelper
130                     .startActivityForResultWithTransition(
131                             activity, intent, requestCode, previousBundle));
132         } else {
133             mHandler.post(() -> com.google.android.setupdesign.transition.TransitionHelper
134                     .startActivityForResultWithTransition(activity, intent, requestCode));
135         }
136     }
137 
138     /**
139      * Calls {@link Activity#startActivityAsUser(Intent, UserHandle)} with an appropriate
140      * transition.
141      */
startActivityAsUserWithTransition( Activity activity, Intent intent, UserHandle userHandle)142     public void startActivityAsUserWithTransition(
143             Activity activity, Intent intent, UserHandle userHandle) {
144         requireNonNull(activity);
145         requireNonNull(intent);
146         requireNonNull(userHandle);
147         if (!ENABLE_CUSTOM_TRANSITIONS) {
148             activity.startActivityAsUser(intent, userHandle);
149             return;
150         }
151         Bundle options = getRelevantActivityOptions(activity, intent);
152         mHandler.post(() -> activity.startActivityAsUser(intent, options, userHandle));
153     }
154 
155     /**
156      * Calls {@link Activity#startActivityForResultAsUser(Intent, int, UserHandle)} with an
157      * appropriate transition.
158      */
startActivityForResultAsUserWithTransition( Activity activity, Intent intent, int requestCode, UserHandle userHandle)159     public void startActivityForResultAsUserWithTransition(
160             Activity activity, Intent intent, int requestCode, UserHandle userHandle) {
161         requireNonNull(activity);
162         requireNonNull(intent);
163         requireNonNull(userHandle);
164         if (!ENABLE_CUSTOM_TRANSITIONS) {
165             activity.startActivityForResultAsUser(intent, requestCode, userHandle);
166             return;
167         }
168         Bundle options = getRelevantActivityOptions(activity, intent);
169         mHandler.post(() ->
170                 activity.startActivityForResultAsUser(intent, requestCode, options, userHandle));
171     }
172 
173     /**
174      * Calls {@link Activity#finish()} with an appropriate transition.
175      */
finishActivity(Activity activity)176     public void finishActivity(Activity activity) {
177         requireNonNull(activity);
178         if (!ENABLE_CUSTOM_TRANSITIONS) {
179             activity.finish();
180             return;
181         }
182         com.google.android.setupdesign.transition.TransitionHelper.finishActivity(activity);
183     }
184 
185     @Nullable
getActivityOptionsForActivity(Activity activity)186     private Bundle getActivityOptionsForActivity(Activity activity) {
187         if (activity.getIntent() == null) {
188             return null;
189         }
190         return activity.getIntent().getBundleExtra(EXTRA_ACTIVITY_OPTIONS);
191     }
192 
hasContentView(Activity activity)193     private boolean hasContentView(Activity activity) {
194         if (activity.getWindow() == null) {
195             return false;
196         }
197         final ViewGroup rootView = (ViewGroup) activity.getWindow().getDecorView().getRootView();
198         return rootView.getChildCount() > 0;
199     }
200 
getRelevantActivityOptions(Activity activity, Intent intent)201     private Bundle getRelevantActivityOptions(Activity activity, Intent intent) {
202         // TODO(b/183537077): Remove hack to start UI-less activities using the previous activity's
203         //  options bundle
204         Bundle options;
205         Bundle previousBundle = getActivityOptionsForActivity(activity);
206         if (!hasContentView(activity) && previousBundle != null) {
207             options = previousBundle;
208         } else {
209             options = makeActivityOptions(activity, intent);
210         }
211         return options;
212     }
213 }
214