1ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing/*
2ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Copyright (C) 2015 The Android Open Source Project
3ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing *
4ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * in compliance with the License. You may obtain a copy of the License at
6ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing *
7ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * http://www.apache.org/licenses/LICENSE-2.0
8ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing *
9ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Unless required by applicable law or agreed to in writing, software distributed under the License
10ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * or implied. See the License for the specific language governing permissions and limitations under
12ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * the License.
13ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing */
14ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingpackage android.support.v17.leanback.app;
15ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
16ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.animation.Animator;
17ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.animation.AnimatorSet;
18ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.app.Activity;
19ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.app.Fragment;
20ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.app.FragmentManager;
21ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.app.FragmentTransaction;
22ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.content.Context;
23ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.content.res.TypedArray;
24ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.os.Bundle;
25ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.annotation.NonNull;
26ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v17.leanback.animation.UntargetableAnimatorSet;
27ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v17.leanback.R;
28ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v17.leanback.widget.GuidanceStylist;
29ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v17.leanback.widget.GuidanceStylist.Guidance;
30ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v17.leanback.widget.GuidedAction;
31ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v17.leanback.widget.GuidedActionsStylist;
32ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v17.leanback.widget.VerticalGridView;
33ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.util.AttributeSet;
34ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.util.Log;
35ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.util.TypedValue;
36ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.ContextThemeWrapper;
37ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.LayoutInflater;
38ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.View;
39ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.ViewGroup;
40ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.ViewTreeObserver;
41ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.widget.ImageView;
42ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.widget.RelativeLayout;
43ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.widget.TextView;
44ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
45ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport java.util.ArrayList;
46ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport java.util.List;
47ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
48ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing/**
49ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * A GuidedStepFragment is used to guide the user through a decision or series of decisions.
50751fc58ffff0614288c610fbd0767969abb9365fKris Giesing * It is composed of a guidance view on the left and a view on the right containing a list of
51751fc58ffff0614288c610fbd0767969abb9365fKris Giesing * possible actions.
52ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
53ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <h3>Basic Usage</h3>
54ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
55ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Clients of GuidedStepFragment typically create a custom subclass to attach to their Activities.
56ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * This custom subclass provides the information necessary to construct the user interface and
57ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * respond to user actions. At a minimum, subclasses should override:
58ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <ul>
59ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link #onCreateGuidance}, to provide instructions to the user</li>
60ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link #onCreateActions}, to provide a set of {@link GuidedAction}s the user can take</li>
61ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link #onGuidedActionClicked}, to respond to those actions</li>
62ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * </ul>
63ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
64ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <h3>Theming and Stylists</h3>
65ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
66ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * GuidedStepFragment delegates its visual styling to classes called stylists. The {@link
67ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * GuidanceStylist} is responsible for the left guidance view, while the {@link
68ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * GuidedActionsStylist} is responsible for the right actions view. The stylists use theme
69ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * attributes to derive values associated with the presentation, such as colors, animations, etc.
70ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Most simple visual aspects of GuidanceStylist and GuidedActionsStylist can be customized
71ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * via theming; see their documentation for more information.
72ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
73ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * GuidedStepFragments must have access to an appropriate theme in order for the stylists to
74ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * function properly.  Specifically, the fragment must receive {@link
75ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * android.support.v17.leanback.R.style#Theme_Leanback_GuidedStep}, or a theme whose parent is
76ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * is set to that theme. Themes can be provided in one of three ways:
77ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <ul>
78ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>The simplest way is to set the theme for the host Activity to the GuidedStep theme or a
79ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * theme that derives from it.</li>
80ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>If the Activity already has a theme and setting its parent theme is inconvenient, the
81ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * existing Activity theme can have an entry added for the attribute {@link
82ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepTheme}. If present,
83ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * this theme will be used by GuidedStepFragment as an overlay to the Activity's theme.</li>
84ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>Finally, custom subclasses of GuidedStepFragment may provide a theme through the {@link
85ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * #onProvideTheme} method. This can be useful if a subclass is used across multiple
86ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Activities.</li>
87ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * </ul>
88ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
89ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * If the theme is provided in multiple ways, the onProvideTheme override has priority, followed by
90ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * the Activty's theme.  (Themes whose parent theme is already set to the guided step theme do not
91ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * need to set the guidedStepTheme attribute; if set, it will be ignored.)
92ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
93ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * If themes do not provide enough customizability, the stylists themselves may be subclassed and
94ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * provided to the GuidedStepFragment through the {@link #onCreateGuidanceStylist} and {@link
95ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * #onCreateActionsStylist} methods.  The stylists have simple hooks so that subclasses
96ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * may override layout files; subclasses may also have more complex logic to determine styling.
97ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
98ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <h3>Guided sequences</h3>
99ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
100ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * GuidedStepFragments can be grouped together to provide a guided sequence. GuidedStepFragments
101ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * grouped as a sequence use custom animations provided by {@link GuidanceStylist} and
102ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * {@link GuidedActionsStylist} (or subclasses) during transitions between steps. Clients
103ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * should use {@link #add} to place subsequent GuidedFragments onto the fragment stack so that
104ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * custom animations are properly configured. (Custom animations are triggered automatically when
105ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * the fragment stack is subsequently popped by any normal mechanism.)
106ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
107ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <i>Note: Currently GuidedStepFragments grouped in this way must all be defined programmatically,
108ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * rather than in XML. This restriction may be removed in the future.</i>
109ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
110ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepTheme
111ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @see GuidanceStylist
112ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @see GuidanceStylist.Guidance
113ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @see GuidedAction
114ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @see GuidedActionsStylist
115ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing */
116ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingpublic class GuidedStepFragment extends Fragment implements GuidedActionAdapter.ClickListener,
117ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        GuidedActionAdapter.FocusListener {
118ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
119ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static final String TAG_LEAN_BACK_ACTIONS_FRAGMENT = "leanBackGuidedStepFragment";
120ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static final String EXTRA_ACTION_SELECTED_INDEX = "selectedIndex";
121ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static final String EXTRA_ACTION_ENTRY_TRANSITION_ENABLED = "entryTransitionEnabled";
122ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static final String EXTRA_ENTRY_TRANSITION_PERFORMED = "entryTransitionPerformed";
123ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static final String TAG = "GuidedStepFragment";
124ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static final boolean DEBUG = true;
125ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static final int ANIMATION_FRAGMENT_ENTER = 1;
126ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static final int ANIMATION_FRAGMENT_EXIT = 2;
127ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static final int ANIMATION_FRAGMENT_ENTER_POP = 3;
128ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static final int ANIMATION_FRAGMENT_EXIT_POP = 4;
129ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
130ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mTheme;
131ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private GuidanceStylist mGuidanceStylist;
132ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private GuidedActionsStylist mActionsStylist;
133ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private GuidedActionAdapter mAdapter;
134ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private VerticalGridView mListView;
135ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private List<GuidedAction> mActions = new ArrayList<GuidedAction>();
136ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mSelectedIndex = -1;
137ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private boolean mEntryTransitionPerformed;
138ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private boolean mEntryTransitionEnabled = true;
139ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
140ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public GuidedStepFragment() {
141ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // We need to supply the theme before any potential call to onInflate in order
142ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // for the defaulting to work properly.
143ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mTheme = onProvideTheme();
144ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mGuidanceStylist = onCreateGuidanceStylist();
145ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mActionsStylist = onCreateActionsStylist();
146ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
147ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
148ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
149ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Creates the presenter used to style the guidance panel. The default implementation returns
150ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * a basic GuidanceStylist.
151ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The GuidanceStylist used in this fragment.
152ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
153ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public GuidanceStylist onCreateGuidanceStylist() {
154ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return new GuidanceStylist();
155ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
156ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
157ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
158ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Creates the presenter used to style the guided actions panel. The default implementation
159ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * returns a basic GuidedActionsStylist.
160ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The GuidedActionsStylist used in this fragment.
161ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
162ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public GuidedActionsStylist onCreateActionsStylist() {
163ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return new GuidedActionsStylist();
164ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
165ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
166ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
167ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns the theme used for styling the fragment. The default returns -1, indicating that the
168ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * host Activity's theme should be used.
169ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The theme resource ID of the theme to use in this fragment, or -1 to use the
170ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * host Activity's theme.
171ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
172ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public int onProvideTheme() {
173ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return -1;
174ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
175ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
176ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
177ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns the information required to provide guidance to the user. This hook is called during
178ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@link #onCreateView}.  May be overridden to return a custom subclass of {@link
179ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * GuidanceStylist.Guidance} for use in a subclass of {@link GuidanceStylist}. The default
180ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * returns a Guidance object with empty fields; subclasses should override.
181ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param savedInstanceState The saved instance state from onCreateView.
182ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The Guidance object representing the information used to guide the user.
183ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
184ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public @NonNull Guidance onCreateGuidance(Bundle savedInstanceState) {
185ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return new Guidance("", "", "", null);
186ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
187ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
188ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
189ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Fills out the set of actions available to the user. This hook is called during {@link
190ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * #onCreate}. The default leaves the list of actions empty; subclasses should override.
191ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param actions A non-null, empty list ready to be populated.
192ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param savedInstanceState The saved instance state from onCreate.
193ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
194ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
195ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
196ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
197ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
198ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Callback invoked when an action is taken by the user. Subclasses should override in
199ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * order to act on the user's decisions.
200ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param action The chosen action.
201ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
202ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
203ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onGuidedActionClicked(GuidedAction action) {
204ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
205ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
206ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
207ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Callback invoked when an action is focused (made to be the current selection) by the user.
208ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
209ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
210ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onGuidedActionFocused(GuidedAction action) {
211ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
212ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
213ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
214ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Adds the specified GuidedStepFragment to the fragment stack, replacing any existing
215ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * GuidedStepFragments in the stack, and configuring the fragment-to-fragment custom animations.
216ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <p>
217ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Note: currently fragments added using this method must be created programmatically rather
218ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * than via XML.
219ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param fragmentManager The FragmentManager to be used in the transaction.
220ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param fragment The GuidedStepFragment to be inserted into the fragment stack.
221ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The ID returned by the call FragmentTransaction.replace.
222ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
223ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public static int add(FragmentManager fragmentManager, GuidedStepFragment fragment) {
224ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return add(fragmentManager, fragment, android.R.id.content);
225ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
226ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
227ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    // Note, this method used to be public, but I haven't found a good way for a client
228ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    // to specify an id.
229ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static int add(FragmentManager fm, GuidedStepFragment f, int id) {
230ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        boolean inGuidedStep = getCurrentGuidedStepFragment(fm) != null;
231ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        FragmentTransaction ft = fm.beginTransaction();
232ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
233ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (inGuidedStep) {
234ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            ft.setCustomAnimations(ANIMATION_FRAGMENT_ENTER,
235ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    ANIMATION_FRAGMENT_EXIT, ANIMATION_FRAGMENT_ENTER_POP,
236ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    ANIMATION_FRAGMENT_EXIT_POP);
237ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            ft.addToBackStack(null);
238ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
239ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return ft.replace(id, f, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit();
240ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
241ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
242ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
243ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns the current GuidedStepFragment on the fragment transaction stack.
244ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The current GuidedStepFragment, if any, on the fragment transaction stack.
245ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
246ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public static GuidedStepFragment getCurrentGuidedStepFragment(FragmentManager fm) {
247ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Fragment f = fm.findFragmentByTag(TAG_LEAN_BACK_ACTIONS_FRAGMENT);
248ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (f instanceof GuidedStepFragment) {
249ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return (GuidedStepFragment) f;
250ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
251ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return null;
252ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
253ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
254ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
255ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns the GuidanceStylist that displays guidance information for the user.
256ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The GuidanceStylist for this fragment.
257ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
258ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public GuidanceStylist getGuidanceStylist() {
259ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return mGuidanceStylist;
260ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
261ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
262ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
263ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns the GuidedActionsStylist that displays the actions the user may take.
264ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The GuidedActionsStylist for this fragment.
265ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
266ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public GuidedActionsStylist getGuidedActionsStylist() {
267ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return mActionsStylist;
268ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
269ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
270ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
271ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns the list of GuidedActions that the user may take in this fragment.
272ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The list of GuidedActions for this fragment.
273ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
274ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public List<GuidedAction> getActions() {
275ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return mActions;
276ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
277ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
278ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
279ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Sets the list of GuidedActions that the user may take in this fragment.
280ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param actions The list of GuidedActions for this fragment.
281ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
282ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void setActions(List<GuidedAction> actions) {
283ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mActions = actions;
284ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (mAdapter != null) {
285ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mAdapter.setActions(mActions);
286ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
287ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
288ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
289ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
290ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns the view corresponding to the action at the indicated position in the list of
291ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * actions for this fragment.
292ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param position The integer position of the action of interest.
293ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The View corresponding to the action at the indicated position, or null if that
294ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * action is not currently onscreen.
295ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
296ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public View getActionItemView(int position) {
297ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return mListView.findViewHolderForPosition(position).itemView;
298ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
299ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
300ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
301ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Scrolls the action list to the position indicated, selecting that action's view.
302ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param position The integer position of the action of interest.
303ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
304ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void setSelectedActionPosition(int position) {
305ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mListView.setSelectedPosition(position);
306ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
307ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
308ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
309ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns the position if the currently selected GuidedAction.
310ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return position The integer position of the currently selected action.
311ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
312ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public int getSelectedActionPosition() {
313ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return mListView.getSelectedPosition();
314ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
315ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
316ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
317ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
318ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
319ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
320ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onCreate(Bundle savedInstanceState) {
321ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        super.onCreate(savedInstanceState);
322ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (DEBUG) Log.v(TAG, "onCreate");
323ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Bundle state = (savedInstanceState != null) ? savedInstanceState : getArguments();
324ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (state != null) {
325ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (mSelectedIndex == -1) {
326ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mSelectedIndex = state.getInt(EXTRA_ACTION_SELECTED_INDEX, -1);
327ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
328ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mEntryTransitionEnabled = state.getBoolean(EXTRA_ACTION_ENTRY_TRANSITION_ENABLED, true);
329ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mEntryTransitionPerformed = state.getBoolean(EXTRA_ENTRY_TRANSITION_PERFORMED, false);
330ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
331ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mActions.clear();
332ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        onCreateActions(mActions, savedInstanceState);
333ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
334ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
335ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
336ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
337ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
338ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
339ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public View onCreateView(LayoutInflater inflater, ViewGroup container,
340ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            Bundle savedInstanceState) {
341ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (DEBUG) Log.v(TAG, "onCreateView");
342ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
343ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        resolveTheme();
344ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        inflater = getThemeInflater(inflater);
345ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
346ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        View v = inflater.inflate(R.layout.lb_guidedstep_fragment, container, false);
347ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ViewGroup guidanceContainer = (ViewGroup) v.findViewById(R.id.content_fragment);
348ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ViewGroup actionContainer = (ViewGroup) v.findViewById(R.id.action_fragment);
349ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
350ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Guidance guidance = onCreateGuidance(savedInstanceState);
351ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        View guidanceView = mGuidanceStylist.onCreateView(inflater, guidanceContainer, guidance);
352ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        guidanceContainer.addView(guidanceView);
353ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
354ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        View actionsView = mActionsStylist.onCreateView(inflater, actionContainer);
355ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        actionContainer.addView(actionsView);
356ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
357ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mAdapter = new GuidedActionAdapter(mActions, this, this, mActionsStylist);
358ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
359ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mListView = mActionsStylist.getActionsGridView();
360ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mListView.setAdapter(mAdapter);
361ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        int pos = (mSelectedIndex >= 0 && mSelectedIndex < mActions.size()) ?
362ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mSelectedIndex : getFirstCheckedAction();
363ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mListView.setSelectedPosition(pos);
364ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
365ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return v;
366ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
367ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
368ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
369ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
370ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
371ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
372ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onSaveInstanceState(Bundle outState) {
373ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        super.onSaveInstanceState(outState);
374ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        outState.putInt(EXTRA_ACTION_SELECTED_INDEX,
375ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                (mListView != null) ? getSelectedActionPosition() : mSelectedIndex);
376ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        outState.putBoolean(EXTRA_ACTION_ENTRY_TRANSITION_ENABLED, mEntryTransitionEnabled);
377ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        outState.putBoolean(EXTRA_ENTRY_TRANSITION_PERFORMED, mEntryTransitionPerformed);
378ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
379ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
380ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
381ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
382ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
383ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
384ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onStart() {
385ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (DEBUG) Log.v(TAG, "onStart");
386ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        super.onStart();
387ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (isEntryTransitionEnabled() && !mEntryTransitionPerformed) {
388ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mEntryTransitionPerformed = true;
389ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            performEntryTransition();
390ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
391ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
392ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
393ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
394ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
395ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
396ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
397ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
398ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (DEBUG) Log.v(TAG, "onCreateAnimator: " + transit + " " + enter + " " + nextAnim);
399ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        View mainView = getView();
400ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
401ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ArrayList<Animator> animators = new ArrayList<Animator>();
402ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        switch (nextAnim) {
403ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            case ANIMATION_FRAGMENT_ENTER:
404ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mGuidanceStylist.onFragmentEnter(animators);
405ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mActionsStylist.onFragmentEnter(animators);
406ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                break;
407ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            case ANIMATION_FRAGMENT_EXIT:
408ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mGuidanceStylist.onFragmentExit(animators);
409ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mActionsStylist.onFragmentExit(animators);
410ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                break;
411ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            case ANIMATION_FRAGMENT_ENTER_POP:
412ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mGuidanceStylist.onFragmentReenter(animators);
413ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mActionsStylist.onFragmentReenter(animators);
414ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                break;
415ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            case ANIMATION_FRAGMENT_EXIT_POP:
416ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mGuidanceStylist.onFragmentReturn(animators);
417ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mActionsStylist.onFragmentReturn(animators);
418ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                break;
419ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            default:
420ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                return super.onCreateAnimator(transit, enter, nextAnim);
421ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
422ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
423ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mEntryTransitionPerformed = true;
424ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return createDummyAnimator(mainView, animators);
425ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
426ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
427ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
428ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns whether entry transitions are enabled for this fragment.
429ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return Whether entry transitions are enabled for this fragment.
430ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
431ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    protected boolean isEntryTransitionEnabled() {
432ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return mEntryTransitionEnabled;
433ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
434ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
435ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
436ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Sets whether entry transitions are enabled for this fragment.
437ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param enabled Whether to enable entry transitions for this fragment.
438ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
439ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    protected void setEntryTransitionEnabled(boolean enabled) {
440ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mEntryTransitionEnabled = enabled;
441ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
442ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
443ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private boolean isGuidedStepTheme(Context context) {
444ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        int resId = R.attr.guidedStepThemeFlag;
445ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        TypedValue typedValue = new TypedValue();
446ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        boolean found = context.getTheme().resolveAttribute(resId, typedValue, true);
447ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (DEBUG) Log.v(TAG, "Found guided step theme flag? " + found);
448ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return found && typedValue.type == TypedValue.TYPE_INT_BOOLEAN && typedValue.data != 0;
449ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
450ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
451ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private void resolveTheme() {
452ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        boolean hasThemeReference = true;
453ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // Look up the guidedStepTheme in the currently specified theme.  If it exists,
454ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // replace the theme with its value.
455ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Activity activity = getActivity();
456ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (mTheme == -1 && !isGuidedStepTheme(activity)) {
457ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            // Look up the guidedStepTheme in the activity's currently specified theme.  If it
458ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            // exists, replace the theme with its value.
459ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            int resId = R.attr.guidedStepTheme;
460ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            TypedValue typedValue = new TypedValue();
461ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            boolean found = activity.getTheme().resolveAttribute(resId, typedValue, true);
462ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (DEBUG) Log.v(TAG, "Found guided step theme reference? " + found);
463ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (found) {
464ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                if (isGuidedStepTheme(new ContextThemeWrapper(activity, typedValue.resourceId))) {
465ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    mTheme = typedValue.resourceId;
466ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                } else {
467ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    found = false;
468ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                }
469ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
470ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (!found) {
471ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                Log.e(TAG, "GuidedStepFragment does not have an appropriate theme set.");
472ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
473ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
474ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
475ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
476ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private LayoutInflater getThemeInflater(LayoutInflater inflater) {
477ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (mTheme == -1) {
478ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return inflater;
479ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        } else {
480ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            Context ctw = new ContextThemeWrapper(getActivity(), mTheme);
481ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return inflater.cloneInContext(ctw);
482ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
483ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
484ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
485ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int getFirstCheckedAction() {
486ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        for (int i = 0, size = mActions.size(); i < size; i++) {
487ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (mActions.get(i).isChecked()) {
488ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                return i;
489ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
490ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
491ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return 0;
492ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
493ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
494ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private void performEntryTransition() {
495ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (DEBUG) Log.v(TAG, "performEntryTransition");
496ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        final View mainView = getView();
497ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
498ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mainView.setVisibility(View.INVISIBLE);
499ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
500ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ArrayList<Animator> animators = new ArrayList<Animator>();
501ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mGuidanceStylist.onActivityEnter(animators);
502ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mActionsStylist.onActivityEnter(animators);
503ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
504ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        final Animator animator = createDummyAnimator(mainView, animators);
505ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
506ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // We need to defer the animation until the first layout has occurred, as we don't yet
507ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // know the final locations of views.
508ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mainView.getViewTreeObserver().addOnGlobalLayoutListener(
509ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                new ViewTreeObserver.OnGlobalLayoutListener() {
510ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    @Override
511ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    public void onGlobalLayout() {
512ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        mainView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
513ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        if (!isAdded()) {
514ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            // We have been detached before this could run,
515ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            // so just bail
516ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            return;
517ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        }
518ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
519ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        mainView.setVisibility(View.VISIBLE);
520ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        animator.start();
521ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    }
522ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                });
523ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
524ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
525ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private Animator createDummyAnimator(final View v, ArrayList<Animator> animators) {
526ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        final AnimatorSet animatorSet = new AnimatorSet();
527ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animatorSet.playTogether(animators);
528ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return new UntargetableAnimatorSet(animatorSet);
529ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
530ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
531ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing}
532