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