PermissionsFrameFragment.java revision ef861375eebd9ac6cce7c0bb163380ab1c951063
1ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav/*
2ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * Copyright (C) 2015 The Android Open Source Project
3ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav *
4ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * Licensed under the Apache License, Version 2.0 (the "License");
5ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * you may not use this file except in compliance with the License.
6ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * You may obtain a copy of the License at
7ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav *
8ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav *      http://www.apache.org/licenses/LICENSE-2.0
9ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav *
10ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * Unless required by applicable law or agreed to in writing, software
11ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * distributed under the License is distributed on an "AS IS" BASIS,
12ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * See the License for the specific language governing permissions and
14ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * limitations under the License.
15ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav */
16ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
17ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavpackage com.android.packageinstaller.permission.ui.television;
18ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
19ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.annotation.Nullable;
20ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.os.Bundle;
21ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.support.v14.preference.PreferenceFragment;
22ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.support.v17.leanback.widget.VerticalGridView;
23ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.support.v7.preference.PreferenceScreen;
24ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.support.v7.widget.RecyclerView;
25ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.support.v7.widget.RecyclerView.AdapterDataObserver;
26ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.view.LayoutInflater;
27ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.view.View;
28ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.view.ViewGroup;
29ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.view.animation.Animation;
30ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.view.animation.Animation.AnimationListener;
31ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.view.animation.AnimationUtils;
32ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.widget.TextView;
33ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
34ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport com.android.packageinstaller.R;
35ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport com.android.packageinstaller.permission.utils.Utils;
36ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
37ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavpublic abstract class PermissionsFrameFragment extends PreferenceFragment {
38ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
39ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private static final float WINDOW_ALIGNMENT_OFFSET_PERCENT = 50;
40ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
41ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private ViewGroup mPreferencesContainer;
42ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
43ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    // TV-specific instance variables
44ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    @Nullable private VerticalGridView mGridView;
45ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
46ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private View mLoadingView;
47ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private ViewGroup mPrefsView;
48ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private boolean mIsLoading;
49ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
50ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    /**
51ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav     * Returns the view group that holds the preferences objects. This will
52ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav     * only be set after {@link #onCreateView} has been called.
53ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav     */
54ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    protected final ViewGroup getPreferencesContainer() {
55ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        return mPreferencesContainer;
56ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
57ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
58ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    @Override
59ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    public View onCreateView(LayoutInflater inflater, ViewGroup container,
60ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            Bundle savedInstanceState) {
61ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.permissions_frame, container,
62ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                        false);
63ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        mPrefsView = (ViewGroup) rootView.findViewById(R.id.prefs_container);
64ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (mPrefsView == null) {
65ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            mPrefsView = rootView;
66ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
67ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        mLoadingView = rootView.findViewById(R.id.loading_container);
68ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        mPreferencesContainer = (ViewGroup) super.onCreateView(
69ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                inflater, mPrefsView, savedInstanceState);
70ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        setLoading(mIsLoading, false, true /* force */);
71ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        mPrefsView.addView(mPreferencesContainer);
72ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        return rootView;
73ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
74ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
75ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    @Override
76ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
77ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        PreferenceScreen preferences = getPreferenceScreen();
78ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (preferences == null) {
79ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            preferences = getPreferenceManager().createPreferenceScreen(getActivity());
80ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            setPreferenceScreen(preferences);
81ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
82ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
83ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
84ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    protected void setLoading(boolean loading, boolean animate) {
85ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        setLoading(loading, animate, false);
86ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
87ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
88ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private void setLoading(boolean loading, boolean animate, boolean force) {
89ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (mIsLoading != loading || force) {
90ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            mIsLoading = loading;
91ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            if (getView() == null) {
92ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                // If there is no created view, there is no reason to animate.
93ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                animate = false;
94ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            }
95ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            if (mPrefsView != null) {
96ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                setViewShown(mPrefsView, !loading, animate);
97ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            }
98ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            if (mLoadingView != null) {
99ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                setViewShown(mLoadingView, loading, animate);
100ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            }
101ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
102ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
103ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
104ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private void setViewShown(final View view, boolean shown, boolean animate) {
105ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (animate) {
106ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            Animation animation = AnimationUtils.loadAnimation(getContext(),
107ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    shown ? android.R.anim.fade_in : android.R.anim.fade_out);
108ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            if (shown) {
109ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                view.setVisibility(View.VISIBLE);
110ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            } else {
111ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                animation.setAnimationListener(new AnimationListener() {
112ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    @Override
113ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    public void onAnimationStart(Animation animation) {
114ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    }
115ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
116ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    @Override
117ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    public void onAnimationRepeat(Animation animation) {
118ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    }
119ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
120ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    @Override
121ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    public void onAnimationEnd(Animation animation) {
122ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                        view.setVisibility(View.INVISIBLE);
123ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    }
124ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                });
125ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            }
126ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            view.startAnimation(animation);
127ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        } else {
128ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            view.clearAnimation();
129ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
130ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
131ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
132ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
133ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    @Override
134ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
135ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            Bundle savedInstanceState) {
136ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (Utils.isTelevision(getContext())) {
137ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            mGridView = (VerticalGridView) inflater.inflate(
138ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    R.layout.leanback_preferences_list, parent, false);
139ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            mGridView.setWindowAlignmentOffset(0);
140ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            mGridView.setWindowAlignmentOffsetPercent(WINDOW_ALIGNMENT_OFFSET_PERCENT);
141ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            mGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
142ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            mGridView.setFocusScrollStrategy(VerticalGridView.FOCUS_SCROLL_ALIGNED);
143ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            return mGridView;
144ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        } else {
145ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            return super.onCreateRecyclerView(inflater, parent, savedInstanceState);
146ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
147ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
148ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
149ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    @Override
150ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    protected RecyclerView.Adapter<?> onCreateAdapter(PreferenceScreen preferenceScreen) {
151ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        final RecyclerView.Adapter<?> adapter = super.onCreateAdapter(preferenceScreen);
152ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
153ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (adapter != null) {
154ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            final TextView emptyView = (TextView) getView().findViewById(R.id.no_permissions);
155ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            onSetEmptyText(emptyView);
156ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            final RecyclerView recyclerView = getListView();
157ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            adapter.registerAdapterDataObserver(new AdapterDataObserver() {
158ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                @Override
159ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                public void onChanged() {
160ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    checkEmpty();
161ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                }
162ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
163ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                @Override
164ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                public void onItemRangeInserted(int positionStart, int itemCount) {
165ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    checkEmpty();
166ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                }
167ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
168ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                @Override
169ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                public void onItemRangeRemoved(int positionStart, int itemCount) {
170ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    checkEmpty();
171ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                }
172ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
173ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                private void checkEmpty() {
174ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    boolean isEmpty = adapter.getItemCount() == 0;
175ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    emptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
176ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    recyclerView.setVisibility(isEmpty ? View.GONE : View.VISIBLE);
177ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    if (!isEmpty && mGridView != null) {
178ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                        mGridView.requestFocus();
179ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                    }
180ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                }
181ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            });
182ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
183ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            boolean isEmpty = adapter.getItemCount() == 0;
184ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            emptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
185ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            recyclerView.setVisibility(isEmpty ? View.GONE : View.VISIBLE);
186ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            if (!isEmpty && mGridView != null) {
187ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                mGridView.requestFocus();
188ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            }
189ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
190ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
191ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        return adapter;
192ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
193ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
194ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    /**
195ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav     * Hook for subclasses to change the default text of the empty view.
196ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav     * Base implementation leaves the default empty view text.
197ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav     *
198ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav     * @param textView the empty text view
199ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav     */
200ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    protected void onSetEmptyText(TextView textView) {
201ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
202ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav}
203ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
204