1 /* 2 * Copyright (C) 2019 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.car.developeroptions; 18 19 import android.appwidget.AppWidgetManager; 20 import android.appwidget.AppWidgetProviderInfo; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.content.pm.PackageManager.NameNotFoundException; 26 import android.content.res.Resources; 27 import android.graphics.drawable.Drawable; 28 import android.os.Bundle; 29 import android.util.DisplayMetrics; 30 import android.util.Log; 31 32 import com.android.car.developeroptions.ActivityPicker.PickAdapter; 33 34 import java.util.List; 35 36 /** 37 * Displays a list of {@link AppWidgetProviderInfo} widgets, along with any 38 * injected special widgets specified through 39 * {@link AppWidgetManager#EXTRA_CUSTOM_INFO} and 40 * {@link AppWidgetManager#EXTRA_CUSTOM_EXTRAS}. 41 * <p> 42 * When an installed {@link AppWidgetProviderInfo} is selected, this activity 43 * will bind it to the given {@link AppWidgetManager#EXTRA_APPWIDGET_ID}, 44 * otherwise it will return the requested extras. 45 */ 46 public class AppWidgetPickActivity extends ActivityPicker 47 implements AppWidgetLoader.ItemConstructor<PickAdapter.Item>{ 48 private static final String TAG = "AppWidgetPickActivity"; 49 static final boolean LOGD = false; 50 51 List<PickAdapter.Item> mItems; 52 53 /** 54 * The allocated {@link AppWidgetManager#EXTRA_APPWIDGET_ID} that this 55 * activity is binding. 56 */ 57 private int mAppWidgetId; 58 private AppWidgetLoader<PickAdapter.Item> mAppWidgetLoader; 59 private AppWidgetManager mAppWidgetManager; 60 private PackageManager mPackageManager; 61 62 @Override onCreate(Bundle icicle)63 public void onCreate(Bundle icicle) { 64 mPackageManager = getPackageManager(); 65 mAppWidgetManager = AppWidgetManager.getInstance(this); 66 mAppWidgetLoader = new AppWidgetLoader<PickAdapter.Item> 67 (this, mAppWidgetManager, this); 68 69 super.onCreate(icicle); 70 71 // Set default return data 72 setResultData(RESULT_CANCELED, null); 73 74 // Read the appWidgetId passed our direction, otherwise bail if not found 75 final Intent intent = getIntent(); 76 if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) { 77 mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 78 AppWidgetManager.INVALID_APPWIDGET_ID); 79 } else { 80 finish(); 81 } 82 } 83 84 /** 85 * Build and return list of items to be shown in dialog. This will mix both 86 * installed {@link AppWidgetProviderInfo} and those provided through 87 * {@link AppWidgetManager#EXTRA_CUSTOM_INFO}, sorting them alphabetically. 88 */ 89 @Override getItems()90 protected List<PickAdapter.Item> getItems() { 91 mItems = mAppWidgetLoader.getItems(getIntent()); 92 return mItems; 93 } 94 95 @Override createItem(Context context, AppWidgetProviderInfo info, Bundle extras)96 public PickAdapter.Item createItem(Context context, AppWidgetProviderInfo info, Bundle extras) { 97 CharSequence label = info.label; 98 Drawable icon = null; 99 100 if (info.icon != 0) { 101 try { 102 final Resources res = context.getResources(); 103 final int density = res.getDisplayMetrics().densityDpi; 104 int iconDensity; 105 switch (density) { 106 case DisplayMetrics.DENSITY_MEDIUM: 107 iconDensity = DisplayMetrics.DENSITY_LOW; 108 case DisplayMetrics.DENSITY_TV: 109 iconDensity = DisplayMetrics.DENSITY_MEDIUM; 110 case DisplayMetrics.DENSITY_HIGH: 111 iconDensity = DisplayMetrics.DENSITY_MEDIUM; 112 case DisplayMetrics.DENSITY_XHIGH: 113 iconDensity = DisplayMetrics.DENSITY_HIGH; 114 case DisplayMetrics.DENSITY_XXHIGH: 115 iconDensity = DisplayMetrics.DENSITY_XHIGH; 116 default: 117 // The density is some abnormal value. Return some other 118 // abnormal value that is a reasonable scaling of it. 119 iconDensity = (int)((density*0.75f)+.5f); 120 } 121 Resources packageResources = mPackageManager. 122 getResourcesForApplication(info.provider.getPackageName()); 123 icon = packageResources.getDrawableForDensity(info.icon, iconDensity); 124 } catch (NameNotFoundException e) { 125 Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon) 126 + " for provider: " + info.provider); 127 } 128 if (icon == null) { 129 Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon) 130 + " for provider: " + info.provider); 131 } 132 } 133 134 PickAdapter.Item item = new PickAdapter.Item(context, label, icon); 135 item.packageName = info.provider.getPackageName(); 136 item.className = info.provider.getClassName(); 137 item.extras = extras; 138 return item; 139 } 140 141 /** 142 * {@inheritDoc} 143 */ 144 @Override onClick(DialogInterface dialog, int which)145 public void onClick(DialogInterface dialog, int which) { 146 Intent intent = getIntentForPosition(which); 147 PickAdapter.Item item = mItems.get(which); 148 149 int result; 150 if (item.extras != null) { 151 // If these extras are present it's because this entry is custom. 152 // Don't try to bind it, just pass it back to the app. 153 setResultData(RESULT_OK, intent); 154 } else { 155 try { 156 Bundle options = null; 157 if (intent.getExtras() != null) { 158 options = intent.getExtras().getBundle( 159 AppWidgetManager.EXTRA_APPWIDGET_OPTIONS); 160 } 161 mAppWidgetManager.bindAppWidgetId(mAppWidgetId, intent.getComponent(), options); 162 result = RESULT_OK; 163 } catch (IllegalArgumentException e) { 164 // This is thrown if they're already bound, or otherwise somehow 165 // bogus. Set the result to canceled, and exit. The app *should* 166 // clean up at this point. We could pass the error along, but 167 // it's not clear that that's useful -- the widget will simply not 168 // appear. 169 result = RESULT_CANCELED; 170 } 171 setResultData(result, null); 172 } 173 174 finish(); 175 } 176 177 178 /** 179 * Convenience method for setting the result code and intent. This method 180 * correctly injects the {@link AppWidgetManager#EXTRA_APPWIDGET_ID} that 181 * most hosts expect returned. 182 */ setResultData(int code, Intent intent)183 void setResultData(int code, Intent intent) { 184 Intent result = intent != null ? intent : new Intent(); 185 result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); 186 setResult(code, result); 187 } 188 } 189