1 /*
2  * Copyright (C) 2012 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 
17 package com.android.settings;
18 
19 import android.appwidget.AppWidgetManager;
20 import android.appwidget.AppWidgetProviderInfo;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.os.Bundle;
24 import android.os.Parcelable;
25 import android.util.Log;
26 
27 import java.text.Collator;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.List;
32 
33 public class AppWidgetLoader<Item extends AppWidgetLoader.LabelledItem> {
34     private static final String TAG = "AppWidgetAdapter";
35     private static final boolean LOGD = AppWidgetPickActivity.LOGD;
36 
37     private Context mContext;
38     private AppWidgetManager mAppWidgetManager;
39     ItemConstructor<Item> mItemConstructor;
40 
41     interface LabelledItem {
getLabel()42         CharSequence getLabel();
43     }
44 
AppWidgetLoader(Context context, AppWidgetManager appWidgetManager, ItemConstructor<Item> itemConstructor)45     public AppWidgetLoader(Context context, AppWidgetManager appWidgetManager,
46             ItemConstructor<Item> itemConstructor) {
47         mContext = context;
48         mAppWidgetManager = appWidgetManager;
49         mItemConstructor = itemConstructor;
50     }
51 
52     /**
53      * Create list entries for any custom widgets requested through
54      * {@link AppWidgetManager#EXTRA_CUSTOM_INFO}.
55      */
putCustomAppWidgets(List<Item> items, Intent intent)56     void putCustomAppWidgets(List<Item> items, Intent intent) {
57         // get and validate the extras they gave us
58         ArrayList<AppWidgetProviderInfo> customInfo = null;
59         ArrayList<Bundle> customExtras = null;
60         try_custom_items: {
61             customInfo = intent.getParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_INFO);
62             if (customInfo == null || customInfo.size() == 0) {
63                 Log.i(TAG, "EXTRA_CUSTOM_INFO not present.");
64                 break try_custom_items;
65             }
66 
67             int customInfoSize = customInfo.size();
68             for (int i=0; i<customInfoSize; i++) {
69                 Parcelable p = customInfo.get(i);
70                 if (p == null || !(p instanceof AppWidgetProviderInfo)) {
71                     customInfo = null;
72                     Log.e(TAG, "error using EXTRA_CUSTOM_INFO index=" + i);
73                     break try_custom_items;
74                 }
75             }
76 
77             customExtras = intent.getParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS);
78             if (customExtras == null) {
79                 customInfo = null;
80                 Log.e(TAG, "EXTRA_CUSTOM_INFO without EXTRA_CUSTOM_EXTRAS");
81                 break try_custom_items;
82             }
83 
84             int customExtrasSize = customExtras.size();
85             if (customInfoSize != customExtrasSize) {
86                 customInfo = null;
87                 customExtras = null;
88                 Log.e(TAG, "list size mismatch: EXTRA_CUSTOM_INFO: " + customInfoSize
89                         + " EXTRA_CUSTOM_EXTRAS: " + customExtrasSize);
90                 break try_custom_items;
91             }
92 
93 
94             for (int i=0; i<customExtrasSize; i++) {
95                 Parcelable p = customExtras.get(i);
96                 if (p == null || !(p instanceof Bundle)) {
97                     customInfo = null;
98                     customExtras = null;
99                     Log.e(TAG, "error using EXTRA_CUSTOM_EXTRAS index=" + i);
100                     break try_custom_items;
101                 }
102             }
103         }
104 
105         if (LOGD) Log.d(TAG, "Using " + customInfo.size() + " custom items");
106         putAppWidgetItems(customInfo, customExtras, items, 0, true);
107     }
108 
109 
110     /**
111      * Create list entries for the given {@link AppWidgetProviderInfo} widgets,
112      * inserting extras if provided.
113      */
putAppWidgetItems(List<AppWidgetProviderInfo> appWidgets, List<Bundle> customExtras, List<Item> items, int categoryFilter, boolean ignoreFilter)114     void putAppWidgetItems(List<AppWidgetProviderInfo> appWidgets,
115             List<Bundle> customExtras, List<Item> items, int categoryFilter,
116             boolean ignoreFilter) {
117         if (appWidgets == null) return;
118         final int size = appWidgets.size();
119         for (int i = 0; i < size; i++) {
120             AppWidgetProviderInfo info = appWidgets.get(i);
121 
122             // We remove any widgets whose category isn't included in the filter
123             if (!ignoreFilter && (info.widgetCategory & categoryFilter) == 0) {
124                 continue;
125             }
126 
127             Item item = mItemConstructor.createItem(mContext, info,
128                     customExtras != null ? customExtras.get(i) : null);
129 
130             items.add(item);
131         }
132     }
133 
134     public interface ItemConstructor<Item> {
createItem(Context context, AppWidgetProviderInfo info, Bundle extras)135         Item createItem(Context context, AppWidgetProviderInfo info, Bundle extras);
136     }
137 
138 
139     /**
140      * Build and return list of items to be shown in dialog. This will mix both
141      * installed {@link AppWidgetProviderInfo} and those provided through
142      * {@link AppWidgetManager#EXTRA_CUSTOM_INFO}, sorting them alphabetically.
143      */
getItems(Intent intent)144     protected List<Item> getItems(Intent intent) {
145         boolean sortCustomAppWidgets =
146                 intent.getBooleanExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, true);
147 
148         List<Item> items = new ArrayList<Item>();
149 
150         // Default category is home screen
151         int categoryFilter = intent.getIntExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER,
152                 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
153 
154         putInstalledAppWidgets(items, categoryFilter);
155 
156         // Sort all items together by label
157         if (sortCustomAppWidgets) {
158             putCustomAppWidgets(items, intent);
159         }
160         Collections.sort(items, new Comparator<Item>() {
161             Collator mCollator = Collator.getInstance();
162 
163             public int compare(Item lhs, Item rhs) {
164                 return mCollator.compare(lhs.getLabel(), rhs.getLabel());
165             }
166         });
167         if (!sortCustomAppWidgets) {
168             List<Item> customItems = new ArrayList<Item>();
169             putCustomAppWidgets(customItems, intent);
170             items.addAll(customItems);
171         }
172         return items;
173     }
174 
175     /**
176      * Create list entries for installed {@link AppWidgetProviderInfo} widgets.
177      */
putInstalledAppWidgets(List<Item> items, int categoryFilter)178     void putInstalledAppWidgets(List<Item> items, int categoryFilter) {
179         List<AppWidgetProviderInfo> installed =
180                 mAppWidgetManager.getInstalledProviders(categoryFilter);
181         putAppWidgetItems(installed, null, items, categoryFilter, false);
182     }
183 }
184