1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.settings.datausage;
16
17import android.app.Application;
18import android.content.Context;
19import android.os.Bundle;
20import android.support.v14.preference.SwitchPreference;
21import android.support.v7.preference.Preference;
22import android.support.v7.preference.PreferenceViewHolder;
23import android.view.Menu;
24import android.view.MenuInflater;
25import android.view.MenuItem;
26import android.view.View;
27
28import com.android.internal.logging.MetricsProto.MetricsEvent;
29import com.android.settings.AppHeader;
30import com.android.settings.R;
31import com.android.settings.SettingsActivity;
32import com.android.settings.SettingsPreferenceFragment;
33import com.android.settings.applications.AppInfoBase;
34import com.android.settings.applications.AppStateBaseBridge;
35import com.android.settings.applications.InstalledAppDetails;
36import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState;
37import com.android.settingslib.applications.ApplicationsState;
38import com.android.settingslib.applications.ApplicationsState.AppEntry;
39import com.android.settingslib.applications.ApplicationsState.AppFilter;
40
41import java.util.ArrayList;
42
43public class UnrestrictedDataAccess extends SettingsPreferenceFragment
44        implements ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
45        Preference.OnPreferenceChangeListener {
46
47    private static final int MENU_SHOW_SYSTEM = Menu.FIRST + 42;
48    private static final String EXTRA_SHOW_SYSTEM = "show_system";
49
50    private ApplicationsState mApplicationsState;
51    private AppStateDataUsageBridge mDataUsageBridge;
52    private ApplicationsState.Session mSession;
53    private DataSaverBackend mDataSaverBackend;
54    private boolean mShowSystem;
55    private boolean mExtraLoaded;
56    private AppFilter mFilter;
57
58    @Override
59    public void onCreate(Bundle icicle) {
60        super.onCreate(icicle);
61        setAnimationAllowed(true);
62        setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
63        mApplicationsState = ApplicationsState.getInstance(
64                (Application) getContext().getApplicationContext());
65        mDataSaverBackend = new DataSaverBackend(getContext());
66        mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend);
67        mSession = mApplicationsState.newSession(this);
68        mShowSystem = icicle != null && icicle.getBoolean(EXTRA_SHOW_SYSTEM);
69        mFilter = mShowSystem ? ApplicationsState.FILTER_ALL_ENABLED
70                : ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER;
71        setHasOptionsMenu(true);
72    }
73
74    @Override
75    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
76        menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE,
77                mShowSystem ? R.string.menu_hide_system : R.string.menu_show_system);
78        super.onCreateOptionsMenu(menu, inflater);
79    }
80
81    @Override
82    public boolean onOptionsItemSelected(MenuItem item) {
83        switch (item.getItemId()) {
84            case MENU_SHOW_SYSTEM:
85                mShowSystem = !mShowSystem;
86                item.setTitle(mShowSystem ? R.string.menu_hide_system : R.string.menu_show_system);
87                mFilter = mShowSystem ? ApplicationsState.FILTER_ALL_ENABLED
88                        : ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER;
89                if (mExtraLoaded) {
90                    rebuild();
91                }
92                break;
93        }
94        return super.onOptionsItemSelected(item);
95    }
96
97    @Override
98    public void onSaveInstanceState(Bundle outState) {
99        super.onSaveInstanceState(outState);
100        outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
101    }
102
103    @Override
104    public void onViewCreated(View view, Bundle savedInstanceState) {
105        super.onViewCreated(view, savedInstanceState);
106        setLoading(true, false);
107    }
108
109    @Override
110    public void onResume() {
111        super.onResume();
112        mSession.resume();
113        mDataUsageBridge.resume();
114    }
115
116    @Override
117    public void onPause() {
118        super.onPause();
119        mDataUsageBridge.pause();
120        mSession.pause();
121    }
122
123    @Override
124    public void onDestroy() {
125        super.onDestroy();
126        mSession.release();
127        mDataUsageBridge.release();
128    }
129
130    @Override
131    public void onExtraInfoUpdated() {
132        mExtraLoaded = true;
133        rebuild();
134    }
135
136    @Override
137    protected int getHelpResource() {
138        return R.string.help_url_unrestricted_data_access;
139    }
140
141    private void rebuild() {
142        ArrayList<AppEntry> apps = mSession.rebuild(mFilter, ApplicationsState.ALPHA_COMPARATOR);
143        if (apps != null) {
144            onRebuildComplete(apps);
145        }
146    }
147
148    @Override
149    public void onRunningStateChanged(boolean running) {
150
151    }
152
153    @Override
154    public void onPackageListChanged() {
155
156    }
157
158    @Override
159    public void onRebuildComplete(ArrayList<AppEntry> apps) {
160        if (getContext() == null) return;
161        cacheRemoveAllPrefs(getPreferenceScreen());
162        final int N = apps.size();
163        for (int i = 0; i < N; i++) {
164            AppEntry entry = apps.get(i);
165            String key = entry.info.packageName + "|" + entry.info.uid;
166            AccessPreference preference = (AccessPreference) getCachedPreference(key);
167            if (preference == null) {
168                preference = new AccessPreference(getPrefContext(), entry);
169                preference.setKey(key);
170                preference.setOnPreferenceChangeListener(this);
171                getPreferenceScreen().addPreference(preference);
172            } else {
173                preference.reuse();
174            }
175            preference.setOrder(i);
176        }
177        setLoading(false, true);
178        removeCachedPrefs(getPreferenceScreen());
179    }
180
181    @Override
182    public void onPackageIconChanged() {
183
184    }
185
186    @Override
187    public void onPackageSizeChanged(String packageName) {
188
189    }
190
191    @Override
192    public void onAllSizesComputed() {
193
194    }
195
196    @Override
197    public void onLauncherInfoChanged() {
198
199    }
200
201    @Override
202    public void onLoadEntriesCompleted() {
203
204    }
205
206    @Override
207    protected int getMetricsCategory() {
208        return MetricsEvent.DATA_USAGE_UNRESTRICTED_ACCESS;
209    }
210
211    @Override
212    public boolean onPreferenceChange(Preference preference, Object newValue) {
213        if (preference instanceof AccessPreference) {
214            AccessPreference accessPreference = (AccessPreference) preference;
215            boolean whitelisted = newValue == Boolean.TRUE;
216            mDataSaverBackend.setIsWhitelisted(accessPreference.mEntry.info.uid,
217                    accessPreference.mEntry.info.packageName, whitelisted);
218            accessPreference.mState.isDataSaverWhitelisted = whitelisted;
219            return true;
220        }
221        return false;
222    }
223
224    private class AccessPreference extends SwitchPreference implements DataSaverBackend.Listener {
225        private final AppEntry mEntry;
226        private final DataUsageState mState;
227
228        public AccessPreference(final Context context, AppEntry entry) {
229            super(context);
230            mEntry = entry;
231            mState = (DataUsageState) mEntry.extraInfo;
232            mEntry.ensureLabel(getContext());
233            setState();
234            if (mEntry.icon != null) {
235                setIcon(mEntry.icon);
236            }
237        }
238
239        @Override
240        public void onAttached() {
241            super.onAttached();
242            mDataSaverBackend.addListener(this);
243        }
244
245        @Override
246        public void onDetached() {
247            mDataSaverBackend.remListener(this);
248            super.onDetached();
249        }
250
251        @Override
252        protected void onClick() {
253            if (mState.isDataSaverBlacklisted) {
254                // app is blacklisted, launch App Data Usage screen
255                InstalledAppDetails.startAppInfoFragment(AppDataUsage.class,
256                        getContext().getString(R.string.app_data_usage),
257                        UnrestrictedDataAccess.this,
258                        mEntry);
259            } else {
260                // app is not blacklisted, let superclass handle toggle switch
261                super.onClick();
262            }
263        }
264
265        // Sets UI state based on whitelist/blacklist status.
266        private void setState() {
267            setTitle(mEntry.label);
268            if (mState != null) {
269                setChecked(mState.isDataSaverWhitelisted);
270                if (mState.isDataSaverBlacklisted) {
271                    setSummary(R.string.restrict_background_blacklisted);
272                } else {
273                    setSummary("");
274                }
275            }
276        }
277
278        public void reuse() {
279            setState();
280            notifyChanged();
281        }
282
283        @Override
284        public void onBindViewHolder(PreferenceViewHolder holder) {
285            if (mEntry.icon == null) {
286                holder.itemView.post(new Runnable() {
287                    @Override
288                    public void run() {
289                        // Ensure we have an icon before binding.
290                        mApplicationsState.ensureIcon(mEntry);
291                        // This might trigger us to bind again, but it gives an easy way to only
292                        // load the icon once its needed, so its probably worth it.
293                        setIcon(mEntry.icon);
294                    }
295                });
296            }
297            holder.findViewById(android.R.id.widget_frame)
298                    .setVisibility(mState != null && mState.isDataSaverBlacklisted
299                            ? View.INVISIBLE : View.VISIBLE);
300            super.onBindViewHolder(holder);
301        }
302
303        @Override
304        public void onDataSaverChanged(boolean isDataSaving) {
305        }
306
307        @Override
308        public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) {
309            if (mState != null && mEntry.info.uid == uid) {
310                mState.isDataSaverWhitelisted = isWhitelisted;
311                reuse();
312            }
313        }
314
315        @Override
316        public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) {
317            if (mState != null && mEntry.info.uid == uid) {
318                mState.isDataSaverBlacklisted = isBlacklisted;
319                reuse();
320            }
321        }
322    }
323
324}
325