• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 package com.android.settings.applications;
18 
19 import android.app.ListFragment;
20 import android.app.LoaderManager;
21 import android.content.AsyncTaskLoader;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.Loader;
27 import android.content.pm.ActivityInfo;
28 import android.content.res.Configuration;
29 import android.content.res.Resources;
30 import android.os.Bundle;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.BaseAdapter;
35 import android.widget.ImageView;
36 import android.widget.ListView;
37 import android.widget.TextView;
38 
39 import java.util.List;
40 
41 import com.android.settings.R;
42 import com.android.settings.SettingsActivity;
43 import com.android.settings.applications.AppOpsState.AppOpEntry;
44 
45 public class AppOpsCategory extends ListFragment implements
46         LoaderManager.LoaderCallbacks<List<AppOpEntry>> {
47 
48     private static final int RESULT_APP_DETAILS = 1;
49 
50     AppOpsState mState;
51 
52     // This is the Adapter being used to display the list's data.
53     AppListAdapter mAdapter;
54 
55     String mCurrentPkgName;
56 
AppOpsCategory()57     public AppOpsCategory() {
58     }
59 
AppOpsCategory(AppOpsState.OpsTemplate template)60     public AppOpsCategory(AppOpsState.OpsTemplate template) {
61         Bundle args = new Bundle();
62         args.putParcelable("template", template);
63         setArguments(args);
64     }
65 
66     /**
67      * Helper for determining if the configuration has changed in an interesting
68      * way so we need to rebuild the app list.
69      */
70     public static class InterestingConfigChanges {
71         final Configuration mLastConfiguration = new Configuration();
72         int mLastDensity;
73 
applyNewConfig(Resources res)74         boolean applyNewConfig(Resources res) {
75             int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
76             boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
77             if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE
78                     |ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {
79                 mLastDensity = res.getDisplayMetrics().densityDpi;
80                 return true;
81             }
82             return false;
83         }
84     }
85 
86     /**
87      * Helper class to look for interesting changes to the installed apps
88      * so that the loader can be updated.
89      */
90     public static class PackageIntentReceiver extends BroadcastReceiver {
91         final AppListLoader mLoader;
92 
PackageIntentReceiver(AppListLoader loader)93         public PackageIntentReceiver(AppListLoader loader) {
94             mLoader = loader;
95             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
96             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
97             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
98             filter.addDataScheme("package");
99             mLoader.getContext().registerReceiver(this, filter);
100             // Register for events related to sdcard installation.
101             IntentFilter sdFilter = new IntentFilter();
102             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
103             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
104             mLoader.getContext().registerReceiver(this, sdFilter);
105         }
106 
onReceive(Context context, Intent intent)107         @Override public void onReceive(Context context, Intent intent) {
108             // Tell the loader about the change.
109             mLoader.onContentChanged();
110         }
111     }
112 
113     /**
114      * A custom Loader that loads all of the installed applications.
115      */
116     public static class AppListLoader extends AsyncTaskLoader<List<AppOpEntry>> {
117         final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();
118         final AppOpsState mState;
119         final AppOpsState.OpsTemplate mTemplate;
120 
121         List<AppOpEntry> mApps;
122         PackageIntentReceiver mPackageObserver;
123 
AppListLoader(Context context, AppOpsState state, AppOpsState.OpsTemplate template)124         public AppListLoader(Context context, AppOpsState state, AppOpsState.OpsTemplate template) {
125             super(context);
126             mState = state;
127             mTemplate = template;
128         }
129 
loadInBackground()130         @Override public List<AppOpEntry> loadInBackground() {
131             return mState.buildState(mTemplate);
132         }
133 
134         /**
135          * Called when there is new data to deliver to the client.  The
136          * super class will take care of delivering it; the implementation
137          * here just adds a little more logic.
138          */
deliverResult(List<AppOpEntry> apps)139         @Override public void deliverResult(List<AppOpEntry> apps) {
140             if (isReset()) {
141                 // An async query came in while the loader is stopped.  We
142                 // don't need the result.
143                 if (apps != null) {
144                     onReleaseResources(apps);
145                 }
146             }
147             List<AppOpEntry> oldApps = apps;
148             mApps = apps;
149 
150             if (isStarted()) {
151                 // If the Loader is currently started, we can immediately
152                 // deliver its results.
153                 super.deliverResult(apps);
154             }
155 
156             // At this point we can release the resources associated with
157             // 'oldApps' if needed; now that the new result is delivered we
158             // know that it is no longer in use.
159             if (oldApps != null) {
160                 onReleaseResources(oldApps);
161             }
162         }
163 
164         /**
165          * Handles a request to start the Loader.
166          */
onStartLoading()167         @Override protected void onStartLoading() {
168             // We don't monitor changed when loading is stopped, so need
169             // to always reload at this point.
170             onContentChanged();
171 
172             if (mApps != null) {
173                 // If we currently have a result available, deliver it
174                 // immediately.
175                 deliverResult(mApps);
176             }
177 
178             // Start watching for changes in the app data.
179             if (mPackageObserver == null) {
180                 mPackageObserver = new PackageIntentReceiver(this);
181             }
182 
183             // Has something interesting in the configuration changed since we
184             // last built the app list?
185             boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());
186 
187             if (takeContentChanged() || mApps == null || configChange) {
188                 // If the data has changed since the last time it was loaded
189                 // or is not currently available, start a load.
190                 forceLoad();
191             }
192         }
193 
194         /**
195          * Handles a request to stop the Loader.
196          */
onStopLoading()197         @Override protected void onStopLoading() {
198             // Attempt to cancel the current load task if possible.
199             cancelLoad();
200         }
201 
202         /**
203          * Handles a request to cancel a load.
204          */
onCanceled(List<AppOpEntry> apps)205         @Override public void onCanceled(List<AppOpEntry> apps) {
206             super.onCanceled(apps);
207 
208             // At this point we can release the resources associated with 'apps'
209             // if needed.
210             onReleaseResources(apps);
211         }
212 
213         /**
214          * Handles a request to completely reset the Loader.
215          */
onReset()216         @Override protected void onReset() {
217             super.onReset();
218 
219             // Ensure the loader is stopped
220             onStopLoading();
221 
222             // At this point we can release the resources associated with 'apps'
223             // if needed.
224             if (mApps != null) {
225                 onReleaseResources(mApps);
226                 mApps = null;
227             }
228 
229             // Stop monitoring for changes.
230             if (mPackageObserver != null) {
231                 getContext().unregisterReceiver(mPackageObserver);
232                 mPackageObserver = null;
233             }
234         }
235 
236         /**
237          * Helper function to take care of releasing resources associated
238          * with an actively loaded data set.
239          */
onReleaseResources(List<AppOpEntry> apps)240         protected void onReleaseResources(List<AppOpEntry> apps) {
241             // For a simple List<> there is nothing to do.  For something
242             // like a Cursor, we would close it here.
243         }
244     }
245 
246     public static class AppListAdapter extends BaseAdapter {
247         private final Resources mResources;
248         private final LayoutInflater mInflater;
249         private final AppOpsState mState;
250 
251         List<AppOpEntry> mList;
252 
AppListAdapter(Context context, AppOpsState state)253         public AppListAdapter(Context context, AppOpsState state) {
254             mResources = context.getResources();
255             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
256             mState = state;
257         }
258 
setData(List<AppOpEntry> data)259         public void setData(List<AppOpEntry> data) {
260             mList = data;
261             notifyDataSetChanged();
262         }
263 
264         @Override
getCount()265         public int getCount() {
266             return mList != null ? mList.size() : 0;
267         }
268 
269         @Override
getItem(int position)270         public AppOpEntry getItem(int position) {
271             return mList.get(position);
272         }
273 
274         @Override
getItemId(int position)275         public long getItemId(int position) {
276             return position;
277         }
278 
279         /**
280          * Populate new items in the list.
281          */
getView(int position, View convertView, ViewGroup parent)282         @Override public View getView(int position, View convertView, ViewGroup parent) {
283             View view;
284 
285             if (convertView == null) {
286                 view = mInflater.inflate(R.layout.app_ops_item, parent, false);
287             } else {
288                 view = convertView;
289             }
290 
291             AppOpEntry item = getItem(position);
292             ((ImageView)view.findViewById(R.id.app_icon)).setImageDrawable(
293                     item.getAppEntry().getIcon());
294             ((TextView)view.findViewById(R.id.app_name)).setText(item.getAppEntry().getLabel());
295             ((TextView)view.findViewById(R.id.op_name)).setText(item.getSummaryText(mState));
296             ((TextView)view.findViewById(R.id.op_time)).setText(
297                     item.getTimeText(mResources, false));
298 
299             return view;
300         }
301     }
302 
303     @Override
onCreate(Bundle savedInstanceState)304     public void onCreate(Bundle savedInstanceState) {
305         super.onCreate(savedInstanceState);
306         mState = new AppOpsState(getActivity());
307     }
308 
onActivityCreated(Bundle savedInstanceState)309     @Override public void onActivityCreated(Bundle savedInstanceState) {
310         super.onActivityCreated(savedInstanceState);
311 
312         // Give some text to display if there is no data.  In a real
313         // application this would come from a resource.
314         setEmptyText("No applications");
315 
316         // We have a menu item to show in action bar.
317         setHasOptionsMenu(true);
318 
319         // Create an empty adapter we will use to display the loaded data.
320         mAdapter = new AppListAdapter(getActivity(), mState);
321         setListAdapter(mAdapter);
322 
323         // Start out with a progress indicator.
324         setListShown(false);
325 
326         // Prepare the loader.
327         getLoaderManager().initLoader(0, null, this);
328     }
329 
330     // utility method used to start sub activity
startApplicationDetailsActivity()331     private void startApplicationDetailsActivity() {
332         // start new fragment to display extended information
333         Bundle args = new Bundle();
334         args.putString(AppOpsDetails.ARG_PACKAGE_NAME, mCurrentPkgName);
335 
336         SettingsActivity sa = (SettingsActivity) getActivity();
337         sa.startPreferencePanel(AppOpsDetails.class.getName(), args,
338                 R.string.app_ops_settings, null, this, RESULT_APP_DETAILS);
339     }
340 
onListItemClick(ListView l, View v, int position, long id)341     @Override public void onListItemClick(ListView l, View v, int position, long id) {
342         AppOpEntry entry = mAdapter.getItem(position);
343         if (entry != null) {
344             mCurrentPkgName = entry.getAppEntry().getApplicationInfo().packageName;
345             startApplicationDetailsActivity();
346         }
347     }
348 
onCreateLoader(int id, Bundle args)349     @Override public Loader<List<AppOpEntry>> onCreateLoader(int id, Bundle args) {
350         Bundle fargs = getArguments();
351         AppOpsState.OpsTemplate template = null;
352         if (fargs != null) {
353             template = (AppOpsState.OpsTemplate)fargs.getParcelable("template");
354         }
355         return new AppListLoader(getActivity(), mState, template);
356     }
357 
onLoadFinished(Loader<List<AppOpEntry>> loader, List<AppOpEntry> data)358     @Override public void onLoadFinished(Loader<List<AppOpEntry>> loader, List<AppOpEntry> data) {
359         // Set the new data in the adapter.
360         mAdapter.setData(data);
361 
362         // The list should now be shown.
363         if (isResumed()) {
364             setListShown(true);
365         } else {
366             setListShownNoAnimation(true);
367         }
368     }
369 
onLoaderReset(Loader<List<AppOpEntry>> loader)370     @Override public void onLoaderReset(Loader<List<AppOpEntry>> loader) {
371         // Clear the data in the adapter.
372         mAdapter.setData(null);
373     }
374 }
375