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 17package com.android.settings; 18 19import android.appwidget.AppWidgetManager; 20import android.appwidget.AppWidgetProviderInfo; 21import android.content.DialogInterface; 22import android.content.Intent; 23import android.content.pm.PackageManager; 24import android.graphics.drawable.Drawable; 25import android.os.Bundle; 26import android.os.Parcelable; 27import android.util.Log; 28 29import java.text.Collator; 30import java.util.List; 31import java.util.ArrayList; 32import java.util.Collections; 33import 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 */ 45public 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 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 */ 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 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 */ 168 void putAppWidgetItems(List<AppWidgetProviderInfo> appWidgets, 169 List<Bundle> customExtras, List<PickAdapter.Item> items) { 170 if (appWidgets == null) return; 171 final int size = appWidgets.size(); 172 for (int i = 0; i < size; i++) { 173 AppWidgetProviderInfo info = appWidgets.get(i); 174 175 CharSequence label = info.label; 176 Drawable icon = null; 177 178 if (info.icon != 0) { 179 icon = mPackageManager.getDrawable(info.provider.getPackageName(), info.icon, null); 180 if (icon == null) { 181 Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon) 182 + " for provider: " + info.provider); 183 } 184 } 185 186 PickAdapter.Item item = new PickAdapter.Item(this, label, icon); 187 188 item.packageName = info.provider.getPackageName(); 189 item.className = info.provider.getClassName(); 190 191 if (customExtras != null) { 192 item.extras = customExtras.get(i); 193 } 194 195 items.add(item); 196 } 197 } 198 199 /** 200 * Build and return list of items to be shown in dialog. This will mix both 201 * installed {@link AppWidgetProviderInfo} and those provided through 202 * {@link AppWidgetManager#EXTRA_CUSTOM_INFO}, sorting them alphabetically. 203 */ 204 @Override 205 protected List<PickAdapter.Item> getItems() { 206 List<PickAdapter.Item> items = new ArrayList<PickAdapter.Item>(); 207 208 putInstalledAppWidgets(items); 209 putCustomAppWidgets(items); 210 211 // Sort all items together by label 212 Collections.sort(items, new Comparator<PickAdapter.Item>() { 213 Collator mCollator = Collator.getInstance(); 214 public int compare(PickAdapter.Item lhs, PickAdapter.Item rhs) { 215 return mCollator.compare(lhs.label, rhs.label); 216 } 217 }); 218 219 return items; 220 } 221 222 /** 223 * Create list entries for installed {@link AppWidgetProviderInfo} widgets. 224 */ 225 void putInstalledAppWidgets(List<PickAdapter.Item> items) { 226 List<AppWidgetProviderInfo> installed = mAppWidgetManager.getInstalledProviders(); 227 putAppWidgetItems(installed, null, items); 228 } 229 230 /** 231 * Convenience method for setting the result code and intent. This method 232 * correctly injects the {@link AppWidgetManager#EXTRA_APPWIDGET_ID} that 233 * most hosts expect returned. 234 */ 235 void setResultData(int code, Intent intent) { 236 Intent result = intent != null ? intent : new Intent(); 237 result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); 238 setResult(code, result); 239 } 240} 241