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