/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.managedprovisioning.common; import static com.android.managedprovisioning.provisioning.Constants.ENABLE_CUSTOM_TRANSITIONS; import static com.google.android.setupdesign.transition.TransitionHelper.EXTRA_ACTIVITY_OPTIONS; import static com.google.android.setupdesign.transition.TransitionHelper.TRANSITION_FADE; import static com.google.android.setupdesign.transition.TransitionHelper.makeActivityOptions; import static java.util.Objects.requireNonNull; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.view.ViewGroup; import android.view.Window; import androidx.annotation.Nullable; /** * Wrapper for {@link com.google.android.setupdesign.transition.TransitionHelper}. * *

Each {@link Activity#onCreate(Bundle)} is expected to call {@link * TransitionHelper#applyContentScreenTransitions(Activity)}. For starting an {@link Activity}, use * any of the {@code startActivity*WithTransition} methods provided in this class, in order to * apply the appropriate transition. When finishing an {@link Activity}, {@link * TransitionHelper#finishActivity(Activity)} must be used to apply the relevant transition. * * @see #startActivityWithTransition(Activity, Intent) * @see #startActivityForResultWithTransition(Activity, Intent, int) * @see #startActivityAsUserWithTransition(Activity, Intent, UserHandle) * @see #startActivityForResultAsUserWithTransition(Activity, Intent, int, UserHandle) */ // TODO(b/183631247): Add tests once infrastructure is fixed public class TransitionHelper { private final Handler mHandler = new Handler(Looper.myLooper()); /** * Applies the relevant {@link Activity} transitions for this content screen. * Must be called in {@link Activity#onCreate(Bundle)}. * *

A content screen is a screen that shows content, not a spinner. * *

Also enables the {@link Window#FEATURE_ACTIVITY_TRANSITIONS} feature so that transitions * are supported. * * @see #applyWaitingScreenTransitions(Activity) */ public void applyContentScreenTransitions(Activity activity) { requireNonNull(activity); if (!ENABLE_CUSTOM_TRANSITIONS) { return; } activity.getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS); com.google.android.setupdesign.transition.TransitionHelper.applyForwardTransition(activity); com.google.android.setupdesign.transition.TransitionHelper .applyBackwardTransition(activity); } /** * Applies the relevant {@link Activity} transitions for this waiting screen. * Must be called in {@link Activity#onResume()}. * *

A waiting screen is a screen that shows a spinner, not content. * * @see #applyContentScreenTransitions(Activity) */ public void applyWaitingScreenTransitions(Activity activity) { requireNonNull(activity); com.google.android.setupdesign.transition.TransitionHelper .applyForwardTransition(activity, TRANSITION_FADE); } /** * Calls {@link Activity#startActivity(Intent)} with an appropriate transition. */ public void startActivityWithTransition(Activity activity, Intent intent) { requireNonNull(activity); requireNonNull(intent); if (!ENABLE_CUSTOM_TRANSITIONS) { activity.startActivity(intent); return; } // TODO(b/183537077): Remove hack to start UI-less activities using the previous activity's // options bundle Bundle previousBundle = getActivityOptionsForActivity(activity); if (!hasContentView(activity) && previousBundle != null) { mHandler.post(() -> com.google.android.setupdesign.transition.TransitionHelper .startActivityWithTransition(activity, intent, previousBundle)); } else { mHandler.post(() -> com.google.android.setupdesign.transition.TransitionHelper .startActivityWithTransition(activity, intent)); } } /** * Calls {@link Activity#startActivityForResult(Intent, int)} with an appropriate transition. */ public void startActivityForResultWithTransition( Activity activity, Intent intent, int requestCode) { requireNonNull(activity); requireNonNull(intent); if (!ENABLE_CUSTOM_TRANSITIONS) { activity.startActivityForResult(intent, requestCode); return; } // TODO(b/183537077): Remove hack to start UI-less activities using the previous activity's // options bundle Bundle previousBundle = getActivityOptionsForActivity(activity); if (!hasContentView(activity) && previousBundle != null) { mHandler.post(() -> com.google.android.setupdesign.transition.TransitionHelper .startActivityForResultWithTransition( activity, intent, requestCode, previousBundle)); } else { mHandler.post(() -> com.google.android.setupdesign.transition.TransitionHelper .startActivityForResultWithTransition(activity, intent, requestCode)); } } /** * Calls {@link Activity#startActivityAsUser(Intent, UserHandle)} with an appropriate * transition. */ public void startActivityAsUserWithTransition( Activity activity, Intent intent, UserHandle userHandle) { requireNonNull(activity); requireNonNull(intent); requireNonNull(userHandle); if (!ENABLE_CUSTOM_TRANSITIONS) { activity.startActivityAsUser(intent, userHandle); return; } Bundle options = getRelevantActivityOptions(activity, intent); mHandler.post(() -> activity.startActivityAsUser(intent, options, userHandle)); } /** * Calls {@link Activity#startActivityForResultAsUser(Intent, int, UserHandle)} with an * appropriate transition. */ public void startActivityForResultAsUserWithTransition( Activity activity, Intent intent, int requestCode, UserHandle userHandle) { requireNonNull(activity); requireNonNull(intent); requireNonNull(userHandle); if (!ENABLE_CUSTOM_TRANSITIONS) { activity.startActivityForResultAsUser(intent, requestCode, userHandle); return; } Bundle options = getRelevantActivityOptions(activity, intent); mHandler.post(() -> activity.startActivityForResultAsUser(intent, requestCode, options, userHandle)); } /** * Calls {@link Activity#finish()} with an appropriate transition. */ public void finishActivity(Activity activity) { requireNonNull(activity); if (!ENABLE_CUSTOM_TRANSITIONS) { activity.finish(); return; } com.google.android.setupdesign.transition.TransitionHelper.finishActivity(activity); } @Nullable private Bundle getActivityOptionsForActivity(Activity activity) { if (activity.getIntent() == null) { return null; } return activity.getIntent().getBundleExtra(EXTRA_ACTIVITY_OPTIONS); } private boolean hasContentView(Activity activity) { if (activity.getWindow() == null) { return false; } final ViewGroup rootView = (ViewGroup) activity.getWindow().getDecorView().getRootView(); return rootView.getChildCount() > 0; } private Bundle getRelevantActivityOptions(Activity activity, Intent intent) { // TODO(b/183537077): Remove hack to start UI-less activities using the previous activity's // options bundle Bundle options; Bundle previousBundle = getActivityOptionsForActivity(activity); if (!hasContentView(activity) && previousBundle != null) { options = previousBundle; } else { options = makeActivityOptions(activity, intent); } return options; } }