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