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.getIcon(), cf.getTitle(), cf.getDescription(), cf.getBreadCrumb());
66            }
67        }
68    }
69
70    /**
71     * This method sets the layout property of this class. <br/>
72     * Activities extending {@link DialogFragment} should call this method
73     * before calling {@link #onCreate(Bundle)} if they want to have a
74     * custom view.
75     *
76     * @param contentAreaId id of the content area
77     * @param actionAreaId id of the action area
78     */
79    protected void setLayoutProperties(int contentAreaId, int actionAreaId) {
80        mBase.setLayoutProperties(contentAreaId, actionAreaId);
81    }
82
83    /**
84     * Animates a view.
85     *
86     * @param v              view to animate
87     * @param initAlpha      initial alpha
88     * @param initTransX     initial translation in the X
89     * @param delay          delay in ms
90     * @param duration       duration in ms
91     * @param interpolator   interpolator to be used, can be null
92     * @param isIcon         if {@code true}, this is the main icon being moved
93     */
94    protected void prepareAndAnimateView(final View v, float initAlpha, float initTransX, int delay,
95            int duration, Interpolator interpolator, final boolean isIcon) {
96        mBase.prepareAndAnimateView(
97                v, initAlpha, initTransX, delay, duration, interpolator, isIcon);
98    }
99
100    /**
101     * Called when intro animation is finished.
102     * <p>
103     * If a subclass is going to alter the view, should wait until this is called.
104     */
105    protected void onIntroAnimationFinished() {
106        mBase.onIntroAnimationFinished();
107    }
108
109    protected boolean isIntroAnimationInProgress() {
110        return mBase.isIntroAnimationInProgress();
111    }
112
113    protected ColorDrawable getBackgroundDrawable() {
114        return mBase.getBackgroundDrawable();
115    }
116
117    protected void setBackgroundDrawable(ColorDrawable drawable) {
118        mBase.setBackgroundDrawable(drawable);
119    }
120
121    /* ********************************************************************* */
122    /* Fragment related code below, cannot be placed into BaseDialogFragment */
123    /* ********************************************************************* */
124
125    public void setActivity(Activity act) {
126        mActivity = act;
127    }
128
129    /**
130     * Capable of returning {@link Activity} prior to this Fragment being
131     * attached to it's parent Activity.  Useful for getting the parent
132     * Activity prior to {@link #onAttach(Activity)} being called.
133     * @return parent {@link Activity}
134     */
135    private Activity getRealActivity() {
136        return (mActivity != null ? mActivity : getActivity());
137    }
138
139    /**
140     * Sets the content fragment into the view.
141     */
142    protected void setContentFragment(Fragment fragment) {
143        FragmentTransaction ft = getContentFragmentTransaction(fragment);
144        ft.commit();
145    }
146
147    /**
148     * Sets the action fragment into the view.
149     * <p>
150     * If an action fragment currently exists, this will be added to the back stack.
151     */
152    protected void setActionFragment(Fragment fragment) {
153        setActionFragment(fragment, true);
154    }
155
156    /**
157     * Sets the action fragment into the view.
158     * <p>
159     * If addToBackStack is true, and action fragment currently exists,
160     * this will be added to the back stack.
161     */
162    protected void setActionFragment(Fragment fragment, boolean addToBackStack) {
163        FragmentTransaction ft = addActionFragmentToTransaction(fragment, null, addToBackStack,
164                getRealActivity().getFragmentManager());
165        ft.commit();
166    }
167
168    protected Fragment getActionFragment() {
169        return getRealActivity().getFragmentManager()
170                .findFragmentByTag(BaseDialogFragment.TAG_ACTION);
171    }
172
173    protected Fragment getContentFragment() {
174        return getRealActivity().getFragmentManager()
175                .findFragmentByTag(BaseDialogFragment.TAG_CONTENT);
176    }
177
178    /**
179     * Set the content and action fragments in the same transaction.
180     * <p>
181     * If an action fragment currently exists, this will be added to the back stack.
182     */
183    protected void setContentAndActionFragments(Fragment contentFragment, Fragment actionFragment) {
184        setContentAndActionFragments(contentFragment, actionFragment, true);
185    }
186
187    /**
188     * Set the content and action fragments in the same transaction.
189     * <p>
190     * If addToBackStack and an action fragment currently exists,
191     * this will be added to the back stack.
192     */
193    protected void setContentAndActionFragments(Fragment contentFragment, Fragment actionFragment,
194            boolean addToBackStack) {
195        FragmentTransaction ft = getContentFragmentTransaction(contentFragment);
196        ft = addActionFragmentToTransaction(actionFragment, ft, addToBackStack,
197                getRealActivity().getFragmentManager());
198        ft.commit();
199    }
200
201    /**
202     * Begins a fragment transaction to edit the content fragment.
203     */
204    private FragmentTransaction getContentFragmentTransaction(Fragment fragment) {
205        FragmentManager fm = getRealActivity().getFragmentManager();
206        boolean hasContent = fm.findFragmentByTag(BaseDialogFragment.TAG_CONTENT) != null;
207        FragmentTransaction ft = fm.beginTransaction();
208
209        if (hasContent) {
210            addAnimations(ft);
211        }
212        ft.replace(mBase.mContentAreaId, fragment, BaseDialogFragment.TAG_CONTENT);
213        return ft;
214    }
215
216    /**
217     * Adds an action fragment replacement to an existing fragment transaction, or creates one if
218     * necessary.
219     * <p>
220     * If an action fragment currently exists, this will be added to the back stack.
221     */
222    private FragmentTransaction addActionFragmentToTransaction(Fragment fragment,
223            FragmentTransaction ft, boolean addToBackStack, FragmentManager fm) {
224        if (ft == null) {
225            ft = fm.beginTransaction();
226        }
227        boolean hasActions = fm.findFragmentByTag(BaseDialogFragment.TAG_ACTION) != null;
228        if (hasActions) {
229            addAnimations(ft);
230            if (addToBackStack) {
231                ft.addToBackStack(null);
232            }
233        }
234        ft.replace(mBase.mActionAreaId, fragment, BaseDialogFragment.TAG_ACTION);
235
236        if (fragment instanceof ActionFragment) {
237            if (!((ActionFragment) fragment).hasListener()) {
238                ((ActionFragment) fragment).setListener(this);
239            }
240        }
241
242        return ft;
243    }
244
245    static void addAnimations(FragmentTransaction ft) {
246        ft.setCustomAnimations(R.anim.fragment_slide_left_in,
247                R.anim.fragment_slide_left_out, R.anim.fragment_slide_right_in,
248                R.anim.fragment_slide_right_out);
249    }
250}
251
252