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