• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 package com.android.settings.dashboard;
17 
18 import android.app.Activity;
19 import android.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.IntentFilter;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.os.HandlerThread;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.os.Process;
28 import android.util.ArrayMap;
29 import android.util.ArraySet;
30 import android.util.Log;
31 
32 import com.android.settings.SettingsActivity;
33 import com.android.settingslib.drawer.DashboardCategory;
34 import com.android.settingslib.drawer.SettingsDrawerActivity;
35 import com.android.settingslib.drawer.Tile;
36 
37 import java.lang.reflect.Field;
38 import java.util.ArrayList;
39 import java.util.List;
40 
41 public class SummaryLoader {
42     private static final boolean DEBUG = DashboardSummary.DEBUG;
43     private static final String TAG = "SummaryLoader";
44 
45     public static final String SUMMARY_PROVIDER_FACTORY = "SUMMARY_PROVIDER_FACTORY";
46 
47     private final Activity mActivity;
48     private final ArrayMap<SummaryProvider, ComponentName> mSummaryMap = new ArrayMap<>();
49     private final List<Tile> mTiles = new ArrayList<>();
50 
51     private final Worker mWorker;
52     private final Handler mHandler;
53     private final HandlerThread mWorkerThread;
54 
55     private DashboardAdapter mAdapter;
56     private boolean mListening;
57     private boolean mWorkerListening;
58     private ArraySet<BroadcastReceiver> mReceivers = new ArraySet<>();
59 
SummaryLoader(Activity activity, List<DashboardCategory> categories)60     public SummaryLoader(Activity activity, List<DashboardCategory> categories) {
61         mHandler = new Handler();
62         mWorkerThread = new HandlerThread("SummaryLoader", Process.THREAD_PRIORITY_BACKGROUND);
63         mWorkerThread.start();
64         mWorker = new Worker(mWorkerThread.getLooper());
65         mActivity = activity;
66         for (int i = 0; i < categories.size(); i++) {
67             List<Tile> tiles = categories.get(i).tiles;
68             for (int j = 0; j < tiles.size(); j++) {
69                 Tile tile = tiles.get(j);
70                 mWorker.obtainMessage(Worker.MSG_GET_PROVIDER, tile).sendToTarget();
71             }
72         }
73     }
74 
release()75     public void release() {
76         mWorkerThread.quitSafely();
77         // Make sure we aren't listening.
78         setListeningW(false);
79     }
80 
setAdapter(DashboardAdapter adapter)81     public void setAdapter(DashboardAdapter adapter) {
82         mAdapter = adapter;
83     }
84 
setSummary(SummaryProvider provider, final CharSequence summary)85     public void setSummary(SummaryProvider provider, final CharSequence summary) {
86         final ComponentName component= mSummaryMap.get(provider);
87         mHandler.post(new Runnable() {
88             @Override
89             public void run() {
90                 // Since tiles are not always cached (like on locale change for instance),
91                 // we need to always get the latest one.
92                 if (!(mActivity instanceof SettingsDrawerActivity)) {
93                     if (DEBUG) {
94                         Log.d(TAG, "Can't get category list.");
95                     }
96                     return;
97                 }
98                 final List<DashboardCategory> categories =
99                         ((SettingsDrawerActivity) mActivity).getDashboardCategories();
100                 final Tile tile = getTileFromCategory(categories, component);
101                 if (tile == null) {
102                     if (DEBUG) {
103                         Log.d(TAG, "Can't find tile for " + component);
104                     }
105                     return;
106                 }
107                 if (DEBUG) {
108                     Log.d(TAG, "setSummary " + tile.title + " - " + summary);
109                 }
110                 tile.summary = summary;
111                 mAdapter.notifyChanged(tile);
112             }
113         });
114     }
115 
116     /**
117      * Only call from the main thread.
118      */
setListening(boolean listening)119     public void setListening(boolean listening) {
120         if (mListening == listening) return;
121         mListening = listening;
122         // Unregister listeners immediately.
123         for (int i = 0; i < mReceivers.size(); i++) {
124             mActivity.unregisterReceiver(mReceivers.valueAt(i));
125         }
126         mReceivers.clear();
127         mWorker.removeMessages(Worker.MSG_SET_LISTENING);
128         mWorker.obtainMessage(Worker.MSG_SET_LISTENING, listening ? 1 : 0, 0).sendToTarget();
129     }
130 
getSummaryProvider(Tile tile)131     private SummaryProvider getSummaryProvider(Tile tile) {
132         if (!mActivity.getPackageName().equals(tile.intent.getComponent().getPackageName())) {
133             // Not within Settings, can't load Summary directly.
134             // TODO: Load summary indirectly.
135             return null;
136         }
137         Bundle metaData = getMetaData(tile);
138         if (metaData == null) {
139             if (DEBUG) Log.d(TAG, "No metadata specified for " + tile.intent.getComponent());
140             return null;
141         }
142         String clsName = metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
143         if (clsName == null) {
144             if (DEBUG) Log.d(TAG, "No fragment specified for " + tile.intent.getComponent());
145             return null;
146         }
147         try {
148             Class<?> cls = Class.forName(clsName);
149             Field field = cls.getField(SUMMARY_PROVIDER_FACTORY);
150             SummaryProviderFactory factory = (SummaryProviderFactory) field.get(null);
151             return factory.createSummaryProvider(mActivity, this);
152         } catch (ClassNotFoundException e) {
153             if (DEBUG) Log.d(TAG, "Couldn't find " + clsName, e);
154         } catch (NoSuchFieldException e) {
155             if (DEBUG) Log.d(TAG, "Couldn't find " + SUMMARY_PROVIDER_FACTORY, e);
156         } catch (ClassCastException e) {
157             if (DEBUG) Log.d(TAG, "Couldn't cast " + SUMMARY_PROVIDER_FACTORY, e);
158         } catch (IllegalAccessException e) {
159             if (DEBUG) Log.d(TAG, "Couldn't get " + SUMMARY_PROVIDER_FACTORY, e);
160         }
161         return null;
162     }
163 
getMetaData(Tile tile)164     private Bundle getMetaData(Tile tile) {
165         return tile.metaData;
166     }
167 
168     /**
169      * Registers a receiver and automatically unregisters it when the activity is stopping.
170      * This ensures that the receivers are unregistered immediately, since most summary loader
171      * operations are asynchronous.
172      */
registerReceiver(final BroadcastReceiver receiver, final IntentFilter filter)173     public void registerReceiver(final BroadcastReceiver receiver, final IntentFilter filter) {
174         mActivity.runOnUiThread(new Runnable() {
175             @Override
176             public void run() {
177                 if (!mListening) {
178                     return;
179                 }
180                 mReceivers.add(receiver);
181                 mActivity.registerReceiver(receiver, filter);
182             }
183         });
184     }
185 
setListeningW(boolean listening)186     private synchronized void setListeningW(boolean listening) {
187         if (mWorkerListening == listening) return;
188         mWorkerListening = listening;
189         if (DEBUG) Log.d(TAG, "Listening " + listening);
190         for (SummaryProvider p : mSummaryMap.keySet()) {
191             try {
192                 p.setListening(listening);
193             } catch (Exception e) {
194                 Log.d(TAG, "Problem in setListening", e);
195             }
196         }
197     }
198 
makeProviderW(Tile tile)199     private synchronized void makeProviderW(Tile tile) {
200         SummaryProvider provider = getSummaryProvider(tile);
201         if (provider != null) {
202             if (DEBUG) Log.d(TAG, "Creating " + tile);
203             mSummaryMap.put(provider, tile.intent.getComponent());
204         }
205     }
206 
getTileFromCategory(List<DashboardCategory> categories, ComponentName component)207     private Tile getTileFromCategory(List<DashboardCategory> categories, ComponentName component) {
208         if (categories == null) {
209             if (DEBUG) {
210                 Log.d(TAG, "Category is null, can't find tile");
211             }
212             return null;
213         }
214         final int categorySize = categories.size();
215         for (int i = 0; i < categorySize; i++) {
216             final DashboardCategory category = categories.get(i);
217             final int tileCount = category.tiles.size();
218             for (int j = 0; j < tileCount; j++) {
219                 final Tile tile = category.tiles.get(j);
220                 if (component.equals(tile.intent.getComponent())) {
221                     return tile;
222                 }
223             }
224         }
225         return null;
226     }
227 
228     public interface SummaryProvider {
setListening(boolean listening)229         void setListening(boolean listening);
230     }
231 
232     public interface SummaryProviderFactory {
createSummaryProvider(Activity activity, SummaryLoader summaryLoader)233         SummaryProvider createSummaryProvider(Activity activity, SummaryLoader summaryLoader);
234     }
235 
236     private class Worker extends Handler {
237         private static final int MSG_GET_PROVIDER = 1;
238         private static final int MSG_SET_LISTENING = 2;
239 
Worker(Looper looper)240         public Worker(Looper looper) {
241             super(looper);
242         }
243 
244         @Override
handleMessage(Message msg)245         public void handleMessage(Message msg) {
246             switch (msg.what) {
247                 case MSG_GET_PROVIDER:
248                     Tile tile = (Tile) msg.obj;
249                     makeProviderW(tile);
250                     break;
251                 case MSG_SET_LISTENING:
252                     boolean listening = msg.arg1 != 0;
253                     setListeningW(listening);
254                     break;
255             }
256         }
257     }
258 }
259