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 17package com.android.tv.common.ui.setup; 18 19import android.app.Activity; 20import android.app.Fragment; 21import android.app.FragmentTransaction; 22import android.os.Bundle; 23import android.os.Handler; 24import android.os.Looper; 25import android.os.Message; 26import android.support.annotation.NonNull; 27import android.transition.Transition; 28import android.transition.TransitionInflater; 29import android.view.View; 30import android.view.ViewTreeObserver.OnPreDrawListener; 31 32import com.android.tv.common.R; 33import com.android.tv.common.WeakHandler; 34import com.android.tv.common.ui.setup.animation.SetupAnimationHelper; 35 36/** 37 * Setup activity for onboarding screens or TIS. 38 * 39 * <p>The inherited class should add theme {@code Theme.Setup.GuidedStep} to its definition in 40 * AndroidManifest.xml. 41 */ 42public abstract class SetupActivity extends Activity implements OnActionClickListener { 43 private static final int MSG_EXECUTE_ACTION = 1; 44 45 private boolean mShowInitialFragment = true; 46 private long mFragmentTransitionDuration; 47 private final Handler mHandler = new SetupActivityHandler(this); 48 49 @Override 50 protected void onCreate(Bundle savedInstanceState) { 51 super.onCreate(savedInstanceState); 52 setContentView(R.layout.activity_setup); 53 mFragmentTransitionDuration = getResources().getInteger( 54 R.integer.setup_fragment_transition_duration); 55 // Show initial fragment only when the saved state is not restored, because the last 56 // fragment is restored if savesInstanceState is not null. 57 if (savedInstanceState == null) { 58 // This is the workaround to show the first fragment with delay to show the fragment 59 // enter transition. See http://b/26255145 60 getWindow().getDecorView().getViewTreeObserver().addOnPreDrawListener( 61 new OnPreDrawListener() { 62 @Override 63 public boolean onPreDraw() { 64 getWindow().getDecorView().getViewTreeObserver() 65 .removeOnPreDrawListener(this); 66 showInitialFragment(); 67 return true; 68 } 69 }); 70 } else { 71 mShowInitialFragment = false; 72 } 73 } 74 75 /** 76 * The inherited class should provide the initial fragment to show. 77 * 78 * <p>If this method returns {@code null} during {@link #onCreate}, then call 79 * {@link #showInitialFragment} explicitly later with non null initial fragment. 80 */ 81 protected abstract Fragment onCreateInitialFragment(); 82 83 /** 84 * Shows the initial fragment. 85 * 86 * <p>The inherited class can call this method later explicitly if it doesn't want the initial 87 * fragment to be shown in onCreate(). 88 */ 89 protected void showInitialFragment() { 90 if (!mShowInitialFragment) { 91 return; 92 } 93 Fragment fragment = onCreateInitialFragment(); 94 if (fragment != null) { 95 showFragment(fragment, false); 96 mShowInitialFragment = false; 97 } 98 } 99 100 /** 101 * Shows the given fragment. 102 */ 103 protected FragmentTransaction showFragment(Fragment fragment, boolean addToBackStack) { 104 FragmentTransaction ft = getFragmentManager().beginTransaction(); 105 if (fragment instanceof SetupFragment) { 106 int[] sharedElements = ((SetupFragment) fragment).getSharedElementIds(); 107 if (sharedElements != null && sharedElements.length > 0) { 108 Transition sharedTransition = TransitionInflater.from(this) 109 .inflateTransition(R.transition.transition_action_background); 110 sharedTransition.setDuration(getSharedElementTransitionDuration()); 111 SetupAnimationHelper.applyAnimationTimeScale(sharedTransition); 112 fragment.setSharedElementEnterTransition(sharedTransition); 113 fragment.setSharedElementReturnTransition(sharedTransition); 114 for (int id : sharedElements) { 115 View sharedView = findViewById(id); 116 if (sharedView != null) { 117 ft.addSharedElement(sharedView, sharedView.getTransitionName()); 118 } 119 } 120 } 121 } 122 String tag = fragment.getClass().getCanonicalName(); 123 if (addToBackStack) { 124 ft.addToBackStack(tag); 125 } 126 ft.replace(R.id.fragment_container, fragment, tag).commit(); 127 128 return ft; 129 } 130 131 @Override 132 public void onActionClick(String category, int actionId) { 133 if (mHandler.hasMessages(MSG_EXECUTE_ACTION)) { 134 return; 135 } 136 executeAction(category, actionId); 137 } 138 139 protected void executeActionWithDelay(Runnable action, int delayMs) { 140 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_EXECUTE_ACTION, action), delayMs); 141 } 142 143 // Override this method if the inherited class wants to handle the action. 144 protected void executeAction(String category, int actionId) { } 145 146 /** 147 * Returns the duration of the shared element transition. 148 * 149 * <p>It's (exit transition) + (delayed animation) + (enter transition). 150 */ 151 private long getSharedElementTransitionDuration() { 152 return (mFragmentTransitionDuration + SetupAnimationHelper.DELAY_BETWEEN_SIBLINGS_MS) * 2; 153 } 154 155 private static class SetupActivityHandler extends WeakHandler<SetupActivity> { 156 SetupActivityHandler(SetupActivity activity) { 157 // Should run on main thread because onAc3SupportChanged will be called on main thread. 158 super(Looper.getMainLooper(), activity); 159 } 160 161 @Override 162 protected void handleMessage(Message msg, @NonNull SetupActivity activity) { 163 if (msg.what == MSG_EXECUTE_ACTION) { 164 ((Runnable) msg.obj).run(); 165 } 166 } 167 } 168} 169