1ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko/*
2ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * Copyright (C) 2015 The Android Open Source Project
3ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko *
4ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
5ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * you may not use this file except in compliance with the License.
6ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * You may obtain a copy of the License at
7ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko *
8ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
9ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko *
10ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * Unless required by applicable law or agreed to in writing, software
11ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
12ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * See the License for the specific language governing permissions and
14ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * limitations under the License.
15ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko */
16ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
17ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkopackage com.android.tv.common.ui.setup;
18ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
19ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.app.Activity;
20ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.app.Fragment;
21ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.app.FragmentTransaction;
22ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.os.Bundle;
23ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.os.Handler;
24ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.os.Looper;
25ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.os.Message;
26ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.NonNull;
27ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.transition.Transition;
28ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.transition.TransitionInflater;
29ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.view.View;
30ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.view.ViewTreeObserver.OnPreDrawListener;
31ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
32ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.R;
33ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.WeakHandler;
34ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
35ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
36ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko/**
37ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * Setup activity for onboarding screens or TIS.
38ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko *
39ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * <p>The inherited class should add theme {@code Theme.Setup.GuidedStep} to its definition in
40ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * AndroidManifest.xml.
41ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko */
42ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkopublic abstract class SetupActivity extends Activity implements OnActionClickListener {
43ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private static final int MSG_EXECUTE_ACTION = 1;
44ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
45ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private boolean mShowInitialFragment = true;
46ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private long mFragmentTransitionDuration;
47ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private final Handler mHandler = new SetupActivityHandler(this);
48ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
49ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    @Override
50ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    protected void onCreate(Bundle savedInstanceState) {
51ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        super.onCreate(savedInstanceState);
5265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        SetupAnimationHelper.initialize(this);
53ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        setContentView(R.layout.activity_setup);
54ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mFragmentTransitionDuration = getResources().getInteger(
55ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                R.integer.setup_fragment_transition_duration);
56ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        // Show initial fragment only when the saved state is not restored, because the last
57ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        // fragment is restored if savesInstanceState is not null.
58ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (savedInstanceState == null) {
59ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            // This is the workaround to show the first fragment with delay to show the fragment
60ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            // enter transition. See http://b/26255145
61ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            getWindow().getDecorView().getViewTreeObserver().addOnPreDrawListener(
62ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    new OnPreDrawListener() {
63ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                        @Override
64ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                        public boolean onPreDraw() {
65ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                            getWindow().getDecorView().getViewTreeObserver()
66ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                                    .removeOnPreDrawListener(this);
67ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                            showInitialFragment();
68ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                            return true;
69ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                        }
70ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    });
71ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        } else {
72ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mShowInitialFragment = false;
73ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
74ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
75ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
76ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
77ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * The inherited class should provide the initial fragment to show.
78ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     *
79ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * <p>If this method returns {@code null} during {@link #onCreate}, then call
80ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * {@link #showInitialFragment} explicitly later with non null initial fragment.
81ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
82ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    protected abstract Fragment onCreateInitialFragment();
83ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
84ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
85ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * Shows the initial fragment.
86ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     *
87ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * <p>The inherited class can call this method later explicitly if it doesn't want the initial
88ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * fragment to be shown in onCreate().
89ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
90ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    protected void showInitialFragment() {
91ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (!mShowInitialFragment) {
92ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            return;
93ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
94ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        Fragment fragment = onCreateInitialFragment();
95ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (fragment != null) {
96ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            showFragment(fragment, false);
97ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mShowInitialFragment = false;
98ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
99ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
100ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
101ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
102ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * Shows the given fragment.
103ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
104ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    protected FragmentTransaction showFragment(Fragment fragment, boolean addToBackStack) {
105ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        FragmentTransaction ft = getFragmentManager().beginTransaction();
106ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (fragment instanceof SetupFragment) {
107ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            int[] sharedElements = ((SetupFragment) fragment).getSharedElementIds();
108ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            if (sharedElements != null && sharedElements.length > 0) {
109ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                Transition sharedTransition = TransitionInflater.from(this)
110ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                        .inflateTransition(R.transition.transition_action_background);
111ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                sharedTransition.setDuration(getSharedElementTransitionDuration());
112ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                SetupAnimationHelper.applyAnimationTimeScale(sharedTransition);
113ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                fragment.setSharedElementEnterTransition(sharedTransition);
114ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                fragment.setSharedElementReturnTransition(sharedTransition);
115ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                for (int id : sharedElements) {
116ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    View sharedView = findViewById(id);
117ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    if (sharedView != null) {
118ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                        ft.addSharedElement(sharedView, sharedView.getTransitionName());
119ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    }
120ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                }
121ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }
122ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
123ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        String tag = fragment.getClass().getCanonicalName();
124ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (addToBackStack) {
125ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            ft.addToBackStack(tag);
126ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
127ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        ft.replace(R.id.fragment_container, fragment, tag).commit();
128ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
129ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        return ft;
130ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
131ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
132ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    @Override
13365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public boolean onActionClick(String category, int actionId, Bundle params) {
134ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (mHandler.hasMessages(MSG_EXECUTE_ACTION)) {
13565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return false;
136ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
13765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return executeAction(category, actionId, params);
138ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
139ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
140ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    protected void executeActionWithDelay(Runnable action, int delayMs) {
141ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_EXECUTE_ACTION, action), delayMs);
142ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
143ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
14465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
14565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Override this method if the inherited class wants to handle the action.
14665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * <p>
14765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * The override method should return {@code true} if the action is handled, otherwise
14865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * {@code false}.
14965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
15065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    protected boolean executeAction(String category, int actionId, Bundle params) {
15165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return false;
15265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
153ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
154ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
155ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * Returns the duration of the shared element transition.
156ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     *
157ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * <p>It's (exit transition) + (delayed animation) + (enter transition).
158ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
159ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private long getSharedElementTransitionDuration() {
160ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        return (mFragmentTransitionDuration + SetupAnimationHelper.DELAY_BETWEEN_SIBLINGS_MS) * 2;
161ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
162ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
163ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private static class SetupActivityHandler extends WeakHandler<SetupActivity> {
164ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        SetupActivityHandler(SetupActivity activity) {
165ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            // Should run on main thread because onAc3SupportChanged will be called on main thread.
166ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            super(Looper.getMainLooper(), activity);
167ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
168ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
169ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        @Override
170ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        protected void handleMessage(Message msg, @NonNull SetupActivity activity) {
171ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            if (msg.what == MSG_EXECUTE_ACTION) {
172ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                ((Runnable) msg.obj).run();
173ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }
174ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
175ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
176ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko}
177