1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.tv.settings.dialog.old;
18
19import android.app.Activity;
20import android.app.Fragment;
21import android.app.FragmentManager;
22import android.app.FragmentTransaction;
23import android.graphics.drawable.ColorDrawable;
24import android.os.Bundle;
25import android.view.View;
26import android.view.ViewGroup;
27import android.view.animation.Interpolator;
28
29import com.android.tv.settings.R;
30
31/**
32* A DialogFragment has 2 fragments, a content fragment and a list fragment.
33* <p>
34* Subclasses should override to supply the content fragment and list items.
35* <p>
36* The DialogFragment will handle animating in and out.
37* <p>
38* This class will use a default layout, but a custom layout can be provided by
39* calling {@link #setLayoutProperties}
40*/
41public class DialogFragment extends Fragment implements ActionAdapter.Listener, LiteFragment {
42
43    private Activity mActivity;
44    private final BaseDialogFragment mBase = new BaseDialogFragment(this);
45
46    @Override
47    public void onActionClicked(Action action) {
48        mBase.onActionClicked(getRealActivity(), action);
49    }
50
51    protected void disableEntryAnimation() {
52        mBase.disableEntryAnimation();
53    }
54
55    public void performEntryTransition() {
56        if (mBase.mFirstOnStart) {
57            mBase.mFirstOnStart = false;
58            // Once the subclass has setup its view hierarchy, we can perform an entry
59            // transition if specified by the intent.
60            Fragment fragment = getContentFragment();
61            if (fragment instanceof ContentFragment) {
62                ContentFragment cf = (ContentFragment) fragment;
63                mBase.performEntryTransition(getRealActivity(),
64                        (ViewGroup) getRealActivity().findViewById(android.R.id.content),
65                        cf.getIconResourceId(), cf.getIconResourceUri(),
66                        cf.getIcon(), cf.getTitle(), cf.getDescription(), cf.getBreadCrumb());
67            }
68        }
69    }
70
71    /**
72     * This method sets the layout property of this class. <br/>
73     * Activities extending {@link DialogFragment} should call this method
74     * before calling {@link #onCreate(Bundle)} if they want to have a
75     * custom view.
76     *
77     * @param contentAreaId id of the content area
78     * @param actionAreaId id of the action area
79     */
80    protected void setLayoutProperties(int contentAreaId, int actionAreaId) {
81        mBase.setLayoutProperties(contentAreaId, actionAreaId);
82    }
83
84    /**
85     * Animates a view.
86     *
87     * @param v              view to animate
88     * @param initAlpha      initial alpha
89     * @param initTransX     initial translation in the X
90     * @param delay          delay in ms
91     * @param duration       duration in ms
92     * @param interpolator   interpolator to be used, can be null
93     * @param isIcon         if {@code true}, this is the main icon being moved
94     */
95    protected void prepareAndAnimateView(final View v, float initAlpha, float initTransX, int delay,
96            int duration, Interpolator interpolator, final boolean isIcon) {
97        mBase.prepareAndAnimateView(
98                v, initAlpha, initTransX, delay, duration, interpolator, isIcon);
99    }
100
101    /**
102     * Called when intro animation is finished.
103     * <p>
104     * If a subclass is going to alter the view, should wait until this is called.
105     */
106    protected void onIntroAnimationFinished() {
107        mBase.onIntroAnimationFinished();
108    }
109
110    protected boolean isIntroAnimationInProgress() {
111        return mBase.isIntroAnimationInProgress();
112    }
113
114    protected ColorDrawable getBackgroundDrawable() {
115        return mBase.getBackgroundDrawable();
116    }
117
118    protected void setBackgroundDrawable(ColorDrawable drawable) {
119        mBase.setBackgroundDrawable(drawable);
120    }
121
122    /* ********************************************************************* */
123    /* Fragment related code below, cannot be placed into BaseDialogFragment */
124    /* ********************************************************************* */
125
126    public void setActivity(Activity act) {
127        mActivity = act;
128    }
129
130    /**
131     * Capable of returning {@link Activity} prior to this Fragment being
132     * attached to it's parent Activity.  Useful for getting the parent
133     * Activity prior to {@link #onAttach(Activity)} being called.
134     * @return parent {@link Activity}
135     */
136    private Activity getRealActivity() {
137        return (mActivity != null ? mActivity : getActivity());
138    }
139
140    /**
141     * Sets the content fragment into the view.
142     */
143    protected void setContentFragment(Fragment fragment) {
144        FragmentTransaction ft = getContentFragmentTransaction(fragment);
145        ft.commit();
146    }
147
148    /**
149     * Sets the action fragment into the view.
150     * <p>
151     * If an action fragment currently exists, this will be added to the back stack.
152     */
153    protected void setActionFragment(Fragment fragment) {
154        setActionFragment(fragment, true);
155    }
156
157    /**
158     * Sets the action fragment into the view.
159     * <p>
160     * If addToBackStack is true, and action fragment currently exists,
161     * this will be added to the back stack.
162     */
163    protected void setActionFragment(Fragment fragment, boolean addToBackStack) {
164        FragmentTransaction ft = addActionFragmentToTransaction(fragment, null, addToBackStack,
165                getRealActivity().getFragmentManager());
166        ft.commit();
167    }
168
169    protected Fragment getActionFragment() {
170        return getRealActivity().getFragmentManager()
171                .findFragmentByTag(BaseDialogFragment.TAG_ACTION);
172    }
173
174    protected Fragment getContentFragment() {
175        return getRealActivity().getFragmentManager()
176                .findFragmentByTag(BaseDialogFragment.TAG_CONTENT);
177    }
178
179    /**
180     * Set the content and action fragments in the same transaction.
181     * <p>
182     * If an action fragment currently exists, this will be added to the back stack.
183     */
184    protected void setContentAndActionFragments(Fragment contentFragment, Fragment actionFragment) {
185        setContentAndActionFragments(contentFragment, actionFragment, true);
186    }
187
188    /**
189     * Set the content and action fragments in the same transaction.
190     * <p>
191     * If addToBackStack and an action fragment currently exists,
192     * this will be added to the back stack.
193     */
194    protected void setContentAndActionFragments(Fragment contentFragment, Fragment actionFragment,
195            boolean addToBackStack) {
196        FragmentTransaction ft = getContentFragmentTransaction(contentFragment);
197        ft = addActionFragmentToTransaction(actionFragment, ft, addToBackStack,
198                getRealActivity().getFragmentManager());
199        ft.commit();
200    }
201
202    /**
203     * Begins a fragment transaction to edit the content fragment.
204     */
205    private FragmentTransaction getContentFragmentTransaction(Fragment fragment) {
206        FragmentManager fm = getRealActivity().getFragmentManager();
207        boolean hasContent = fm.findFragmentByTag(BaseDialogFragment.TAG_CONTENT) != null;
208        FragmentTransaction ft = fm.beginTransaction();
209
210        if (hasContent) {
211            addAnimations(ft);
212        }
213        ft.replace(mBase.mContentAreaId, fragment, BaseDialogFragment.TAG_CONTENT);
214        return ft;
215    }
216
217    /**
218     * Adds an action fragment replacement to an existing fragment transaction, or creates one if
219     * necessary.
220     * <p>
221     * If an action fragment currently exists, this will be added to the back stack.
222     */
223    private FragmentTransaction addActionFragmentToTransaction(Fragment fragment,
224            FragmentTransaction ft, boolean addToBackStack, FragmentManager fm) {
225        if (ft == null) {
226            ft = fm.beginTransaction();
227        }
228        boolean hasActions = fm.findFragmentByTag(BaseDialogFragment.TAG_ACTION) != null;
229        if (hasActions) {
230            addAnimations(ft);
231            if (addToBackStack) {
232                ft.addToBackStack(null);
233            }
234        }
235        ft.replace(mBase.mActionAreaId, fragment, BaseDialogFragment.TAG_ACTION);
236
237        if (fragment instanceof ActionFragment) {
238            if (!((ActionFragment) fragment).hasListener()) {
239                ((ActionFragment) fragment).setListener(this);
240            }
241        }
242
243        return ft;
244    }
245
246    static void addAnimations(FragmentTransaction ft) {
247        ft.setCustomAnimations(R.anim.fragment_slide_left_in,
248                R.anim.fragment_slide_left_out, R.anim.fragment_slide_right_in,
249                R.anim.fragment_slide_right_out);
250    }
251}
252
253