10b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka/* 20b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * Copyright (C) 2012 The Android Open Source Project 30b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * 40b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 50b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * you may not use this file except in compliance with the License. 60b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * You may obtain a copy of the License at 70b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * 80b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * http://www.apache.org/licenses/LICENSE-2.0 90b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * 100b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * Unless required by applicable law or agreed to in writing, software 110b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 120b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * See the License for the specific language governing permissions and 140b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * limitations under the License. 150b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka */ 160b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 170b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkapackage com.android.settings; 180b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 190b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport android.appwidget.AppWidgetManager; 200b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport android.appwidget.AppWidgetProviderInfo; 210b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport android.content.Context; 220b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport android.content.Intent; 230b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport android.os.Bundle; 240b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport android.os.Parcelable; 250b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport android.util.Log; 260b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 270b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport java.text.Collator; 280b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport java.util.ArrayList; 290b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport java.util.Collections; 300b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport java.util.Comparator; 310b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkaimport java.util.List; 320b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 330b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurkapublic class AppWidgetLoader<Item extends AppWidgetLoader.LabelledItem> { 340b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka private static final String TAG = "AppWidgetAdapter"; 350b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka private static final boolean LOGD = AppWidgetPickActivity.LOGD; 360b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 370b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka private Context mContext; 380b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka private AppWidgetManager mAppWidgetManager; 390b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka ItemConstructor<Item> mItemConstructor; 400b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 410b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka interface LabelledItem { 420b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka CharSequence getLabel(); 430b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 440b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 450b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka public AppWidgetLoader(Context context, AppWidgetManager appWidgetManager, 460b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka ItemConstructor<Item> itemConstructor) { 470b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka mContext = context; 480b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka mAppWidgetManager = appWidgetManager; 490b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka mItemConstructor = itemConstructor; 500b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 510b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 520b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka /** 530b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * Create list entries for any custom widgets requested through 540b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * {@link AppWidgetManager#EXTRA_CUSTOM_INFO}. 550b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka */ 560b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka void putCustomAppWidgets(List<Item> items, Intent intent) { 570b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka // get and validate the extras they gave us 580b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka ArrayList<AppWidgetProviderInfo> customInfo = null; 590b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka ArrayList<Bundle> customExtras = null; 600b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka try_custom_items: { 610b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka customInfo = intent.getParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_INFO); 620b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka if (customInfo == null || customInfo.size() == 0) { 630b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Log.i(TAG, "EXTRA_CUSTOM_INFO not present."); 640b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka break try_custom_items; 650b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 660b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 670b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka int customInfoSize = customInfo.size(); 680b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka for (int i=0; i<customInfoSize; i++) { 690b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Parcelable p = customInfo.get(i); 700b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka if (p == null || !(p instanceof AppWidgetProviderInfo)) { 710b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka customInfo = null; 720b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Log.e(TAG, "error using EXTRA_CUSTOM_INFO index=" + i); 730b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka break try_custom_items; 740b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 750b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 760b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 770b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka customExtras = intent.getParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS); 780b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka if (customExtras == null) { 790b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka customInfo = null; 800b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Log.e(TAG, "EXTRA_CUSTOM_INFO without EXTRA_CUSTOM_EXTRAS"); 810b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka break try_custom_items; 820b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 830b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 840b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka int customExtrasSize = customExtras.size(); 850b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka if (customInfoSize != customExtrasSize) { 860b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka customInfo = null; 870b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka customExtras = null; 880b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Log.e(TAG, "list size mismatch: EXTRA_CUSTOM_INFO: " + customInfoSize 890b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka + " EXTRA_CUSTOM_EXTRAS: " + customExtrasSize); 900b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka break try_custom_items; 910b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 920b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 930b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 940b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka for (int i=0; i<customExtrasSize; i++) { 950b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Parcelable p = customExtras.get(i); 960b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka if (p == null || !(p instanceof Bundle)) { 970b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka customInfo = null; 980b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka customExtras = null; 990b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Log.e(TAG, "error using EXTRA_CUSTOM_EXTRAS index=" + i); 1000b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka break try_custom_items; 1010b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1020b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1030b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1040b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1050b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka if (LOGD) Log.d(TAG, "Using " + customInfo.size() + " custom items"); 1060b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka putAppWidgetItems(customInfo, customExtras, items, 0, true); 1070b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1080b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1090b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1100b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka /** 1110b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * Create list entries for the given {@link AppWidgetProviderInfo} widgets, 1120b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * inserting extras if provided. 1130b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka */ 1140b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka void putAppWidgetItems(List<AppWidgetProviderInfo> appWidgets, 1150b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka List<Bundle> customExtras, List<Item> items, int categoryFilter, 1160b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka boolean ignoreFilter) { 1170b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka if (appWidgets == null) return; 1180b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka final int size = appWidgets.size(); 1190b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka for (int i = 0; i < size; i++) { 1200b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka AppWidgetProviderInfo info = appWidgets.get(i); 1210b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1220b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka // We remove any widgets whose category isn't included in the filter 1230b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka if (!ignoreFilter && (info.widgetCategory & categoryFilter) == 0) { 1240b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka continue; 1250b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1260b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1270b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Item item = mItemConstructor.createItem(mContext, info, 1280b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka customExtras != null ? customExtras.get(i) : null); 1290b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1300b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka items.add(item); 1310b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1320b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1330b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1340b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka public interface ItemConstructor<Item> { 1350b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Item createItem(Context context, AppWidgetProviderInfo info, Bundle extras); 1360b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1370b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1380b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1390b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka /** 1400b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * Build and return list of items to be shown in dialog. This will mix both 1410b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * installed {@link AppWidgetProviderInfo} and those provided through 1420b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * {@link AppWidgetManager#EXTRA_CUSTOM_INFO}, sorting them alphabetically. 1430b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka */ 1440b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka protected List<Item> getItems(Intent intent) { 1450b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka boolean sortCustomAppWidgets = 1460b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka intent.getBooleanExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, true); 1470b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1480b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka List<Item> items = new ArrayList<Item>(); 1490b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1500b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka // Default category is home screen 1510b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka int categoryFilter = intent.getIntExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER, 1520b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN); 1530b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1540b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka putInstalledAppWidgets(items, categoryFilter); 1550b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1560b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka // Sort all items together by label 1570b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka if (sortCustomAppWidgets) { 1580b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka putCustomAppWidgets(items, intent); 1590b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1600b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Collections.sort(items, new Comparator<Item>() { 1610b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka Collator mCollator = Collator.getInstance(); 1620b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1630b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka public int compare(Item lhs, Item rhs) { 1640b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka return mCollator.compare(lhs.getLabel(), rhs.getLabel()); 1650b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1660b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka }); 1670b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka if (!sortCustomAppWidgets) { 1680b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka List<Item> customItems = new ArrayList<Item>(); 1690b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka putCustomAppWidgets(customItems, intent); 1700b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka items.addAll(customItems); 1710b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1720b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka return items; 1730b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1740b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka 1750b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka /** 1760b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka * Create list entries for installed {@link AppWidgetProviderInfo} widgets. 1770b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka */ 1780b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka void putInstalledAppWidgets(List<Item> items, int categoryFilter) { 179441588d2213e1088f3af737a59e589ddcf0cf867Adam Cohen List<AppWidgetProviderInfo> installed = 180441588d2213e1088f3af737a59e589ddcf0cf867Adam Cohen mAppWidgetManager.getInstalledProviders(categoryFilter); 1810b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka putAppWidgetItems(installed, null, items, categoryFilter, false); 1820b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka } 1830b2bd8d11d6947a6ce71db0691ba341aec7a2964Michael Jurka} 184