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