1 /* 2 * Copyright (C) 2009 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.DialogInterface; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.graphics.drawable.Drawable; 25 import android.os.Bundle; 26 import android.os.Parcelable; 27 import android.util.Log; 28 29 import java.text.Collator; 30 import java.util.List; 31 import java.util.ArrayList; 32 import java.util.Collections; 33 import java.util.Comparator; 34 35 /** 36 * Displays a list of {@link AppWidgetProviderInfo} widgets, along with any 37 * injected special widgets specified through 38 * {@link AppWidgetManager#EXTRA_CUSTOM_INFO} and 39 * {@link AppWidgetManager#EXTRA_CUSTOM_EXTRAS}. 40 * <p> 41 * When an installed {@link AppWidgetProviderInfo} is selected, this activity 42 * will bind it to the given {@link AppWidgetManager#EXTRA_APPWIDGET_ID}, 43 * otherwise it will return the requested extras. 44 */ 45 public class AppWidgetPickActivity extends ActivityPicker { 46 private static final String TAG = "AppWidgetPickActivity"; 47 private static final boolean LOGD = false; 48 49 private PackageManager mPackageManager; 50 private AppWidgetManager mAppWidgetManager; 51 52 /** 53 * The allocated {@link AppWidgetManager#EXTRA_APPWIDGET_ID} that this 54 * activity is binding. 55 */ 56 private int mAppWidgetId; 57 58 @Override onCreate(Bundle icicle)59 public void onCreate(Bundle icicle) { 60 mPackageManager = getPackageManager(); 61 mAppWidgetManager = AppWidgetManager.getInstance(this); 62 63 super.onCreate(icicle); 64 65 // Set default return data 66 setResultData(RESULT_CANCELED, null); 67 68 // Read the appWidgetId passed our direction, otherwise bail if not found 69 final Intent intent = getIntent(); 70 if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) { 71 mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 72 AppWidgetManager.INVALID_APPWIDGET_ID); 73 } else { 74 finish(); 75 } 76 } 77 78 /** 79 * Create list entries for any custom widgets requested through 80 * {@link AppWidgetManager#EXTRA_CUSTOM_INFO}. 81 */ putCustomAppWidgets(List<PickAdapter.Item> items)82 void putCustomAppWidgets(List<PickAdapter.Item> items) { 83 final Bundle extras = getIntent().getExtras(); 84 85 // get and validate the extras they gave us 86 ArrayList<AppWidgetProviderInfo> customInfo = null; 87 ArrayList<Bundle> customExtras = null; 88 try_custom_items: { 89 customInfo = extras.getParcelableArrayList(AppWidgetManager.EXTRA_CUSTOM_INFO); 90 if (customInfo == null || customInfo.size() == 0) { 91 Log.i(TAG, "EXTRA_CUSTOM_INFO not present."); 92 break try_custom_items; 93 } 94 95 int customInfoSize = customInfo.size(); 96 for (int i=0; i<customInfoSize; i++) { 97 Parcelable p = customInfo.get(i); 98 if (p == null || !(p instanceof AppWidgetProviderInfo)) { 99 customInfo = null; 100 Log.e(TAG, "error using EXTRA_CUSTOM_INFO index=" + i); 101 break try_custom_items; 102 } 103 } 104 105 customExtras = extras.getParcelableArrayList(AppWidgetManager.EXTRA_CUSTOM_EXTRAS); 106 if (customExtras == null) { 107 customInfo = null; 108 Log.e(TAG, "EXTRA_CUSTOM_INFO without EXTRA_CUSTOM_EXTRAS"); 109 break try_custom_items; 110 } 111 112 int customExtrasSize = customExtras.size(); 113 if (customInfoSize != customExtrasSize) { 114 Log.e(TAG, "list size mismatch: EXTRA_CUSTOM_INFO: " + customInfoSize 115 + " EXTRA_CUSTOM_EXTRAS: " + customExtrasSize); 116 break try_custom_items; 117 } 118 119 120 for (int i=0; i<customExtrasSize; i++) { 121 Parcelable p = customExtras.get(i); 122 if (p == null || !(p instanceof Bundle)) { 123 customInfo = null; 124 customExtras = null; 125 Log.e(TAG, "error using EXTRA_CUSTOM_EXTRAS index=" + i); 126 break try_custom_items; 127 } 128 } 129 } 130 131 if (LOGD) Log.d(TAG, "Using " + customInfo.size() + " custom items"); 132 putAppWidgetItems(customInfo, customExtras, items); 133 } 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override onClick(DialogInterface dialog, int which)139 public void onClick(DialogInterface dialog, int which) { 140 Intent intent = getIntentForPosition(which); 141 142 int result; 143 if (intent.getExtras() != null) { 144 // If there are any extras, it's because this entry is custom. 145 // Don't try to bind it, just pass it back to the app. 146 setResultData(RESULT_OK, intent); 147 } else { 148 try { 149 mAppWidgetManager.bindAppWidgetId(mAppWidgetId, intent.getComponent()); 150 result = RESULT_OK; 151 } catch (IllegalArgumentException e) { 152 // This is thrown if they're already bound, or otherwise somehow 153 // bogus. Set the result to canceled, and exit. The app *should* 154 // clean up at this point. We could pass the error along, but 155 // it's not clear that that's useful -- the widget will simply not 156 // appear. 157 result = RESULT_CANCELED; 158 } 159 setResultData(result, null); 160 } 161 finish(); 162 } 163 164 /** 165 * Create list entries for the given {@link AppWidgetProviderInfo} widgets, 166 * inserting extras if provided. 167 */ putAppWidgetItems(List<AppWidgetProviderInfo> appWidgets, List<Bundle> customExtras, List<PickAdapter.Item> items)168 void putAppWidgetItems(List<AppWidgetProviderInfo> appWidgets, 169 List<Bundle> customExtras, List<PickAdapter.Item> items) { 170 final int size = appWidgets.size(); 171 for (int i = 0; i < size; i++) { 172 AppWidgetProviderInfo info = appWidgets.get(i); 173 174 CharSequence label = info.label; 175 Drawable icon = null; 176 177 if (info.icon != 0) { 178 icon = mPackageManager.getDrawable(info.provider.getPackageName(), info.icon, null); 179 if (icon == null) { 180 Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon) 181 + " for provider: " + info.provider); 182 } 183 } 184 185 PickAdapter.Item item = new PickAdapter.Item(this, label, icon); 186 187 item.packageName = info.provider.getPackageName(); 188 item.className = info.provider.getClassName(); 189 190 if (customExtras != null) { 191 item.extras = customExtras.get(i); 192 } 193 194 items.add(item); 195 } 196 } 197 198 /** 199 * Build and return list of items to be shown in dialog. This will mix both 200 * installed {@link AppWidgetProviderInfo} and those provided through 201 * {@link AppWidgetManager#EXTRA_CUSTOM_INFO}, sorting them alphabetically. 202 */ 203 @Override getItems()204 protected List<PickAdapter.Item> getItems() { 205 List<PickAdapter.Item> items = new ArrayList<PickAdapter.Item>(); 206 207 putInstalledAppWidgets(items); 208 putCustomAppWidgets(items); 209 210 // Sort all items together by label 211 Collections.sort(items, new Comparator<PickAdapter.Item>() { 212 Collator mCollator = Collator.getInstance(); 213 public int compare(PickAdapter.Item lhs, PickAdapter.Item rhs) { 214 return mCollator.compare(lhs.label, rhs.label); 215 } 216 }); 217 218 return items; 219 } 220 221 /** 222 * Create list entries for installed {@link AppWidgetProviderInfo} widgets. 223 */ putInstalledAppWidgets(List<PickAdapter.Item> items)224 void putInstalledAppWidgets(List<PickAdapter.Item> items) { 225 List<AppWidgetProviderInfo> installed = mAppWidgetManager.getInstalledProviders(); 226 putAppWidgetItems(installed, null, items); 227 } 228 229 /** 230 * Convenience method for setting the result code and intent. This method 231 * correctly injects the {@link AppWidgetManager#EXTRA_APPWIDGET_ID} that 232 * most hosts expect returned. 233 */ setResultData(int code, Intent intent)234 void setResultData(int code, Intent intent) { 235 Intent result = intent != null ? intent : new Intent(); 236 result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); 237 setResult(code, result); 238 } 239 } 240