• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Activity;
20 import android.app.Fragment;
21 import android.app.FragmentTransaction;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.os.Looper;
25 import android.os.Message;
26 import android.support.annotation.NonNull;
27 import android.transition.Transition;
28 import android.transition.TransitionInflater;
29 import android.view.View;
30 import android.view.ViewTreeObserver.OnPreDrawListener;
31 
32 import com.android.tv.common.R;
33 import com.android.tv.common.WeakHandler;
34 import 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  */
42 public 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
onCreate(Bundle savedInstanceState)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      */
onCreateInitialFragment()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      */
showInitialFragment()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      */
showFragment(Fragment fragment, boolean addToBackStack)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
onActionClick(String category, int actionId)132     public void onActionClick(String category, int actionId) {
133         if (mHandler.hasMessages(MSG_EXECUTE_ACTION)) {
134             return;
135         }
136         executeAction(category, actionId);
137     }
138 
executeActionWithDelay(Runnable action, int delayMs)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.
executeAction(String category, int actionId)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      */
getSharedElementTransitionDuration()151     private long getSharedElementTransitionDuration() {
152         return (mFragmentTransitionDuration + SetupAnimationHelper.DELAY_BETWEEN_SIBLINGS_MS) * 2;
153     }
154 
155     private static class SetupActivityHandler extends WeakHandler<SetupActivity> {
SetupActivityHandler(SetupActivity activity)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
handleMessage(Message msg, @NonNull SetupActivity activity)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