1 /* 2 * Copyright (C) 2010 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.widgetpreview; 18 19 import android.app.Activity; 20 import android.appwidget.AppWidgetHost; 21 import android.appwidget.AppWidgetHostView; 22 import android.appwidget.AppWidgetManager; 23 import android.appwidget.AppWidgetProviderInfo; 24 import android.content.ActivityNotFoundException; 25 import android.content.Intent; 26 import android.content.res.Configuration; 27 import android.content.res.Resources; 28 import android.graphics.Bitmap; 29 import android.graphics.Bitmap.CompressFormat; 30 import android.graphics.Bitmap.Config; 31 import android.graphics.Canvas; 32 import android.net.Uri; 33 import android.os.Bundle; 34 import android.os.Environment; 35 import android.util.Log; 36 import android.view.View; 37 import android.view.View.OnClickListener; 38 import android.view.ViewGroup; 39 import android.widget.Button; 40 import android.widget.FrameLayout; 41 import android.widget.Toast; 42 43 import java.io.File; 44 import java.io.FileOutputStream; 45 import java.io.IOException; 46 47 public class WidgetPreviewActivity extends Activity implements OnClickListener { 48 49 private static final String LOG_TAG = "WidgetPreviewActivity"; 50 private static final boolean DEBUG = true; 51 private static final int APPWIDGET_HOST_ID = 2048; 52 private static final int REQUEST_WIDGET = 0; 53 private static final int REQUEST_CONFIGURE = 1; 54 55 private AppWidgetHost mAppWidgetHost = null; 56 private FrameLayout mAppWidgetFrame = null; 57 private AppWidgetHostView mAppWidgetView = null; 58 private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; 59 private String mAppWidgetName; 60 private int mPreviewWidth; 61 private int mPreviewHeight; 62 63 private Button mSnapshotButton = null; 64 private Button mEmailButton = null; 65 66 @Override onCreate(Bundle savedInstanceState)67 public void onCreate(Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 setContentView(R.layout.activity_main); 70 mAppWidgetFrame = (FrameLayout)findViewById(R.id.main_frame); 71 mSnapshotButton = (Button)findViewById(R.id.snapshot_button); 72 mSnapshotButton.setOnClickListener(this); 73 mEmailButton = (Button)findViewById(R.id.email_button); 74 mEmailButton.setOnClickListener(this); 75 76 mAppWidgetHost = new AppWidgetHost(getApplicationContext(), APPWIDGET_HOST_ID); 77 78 final Object retainedObj = getLastNonConfigurationInstance(); 79 if (retainedObj instanceof AppWidgetProviderInfo) { 80 AppWidgetProviderInfo info = (AppWidgetProviderInfo) retainedObj; 81 int id = mAppWidgetHost.allocateAppWidgetId(); 82 AppWidgetManager.getInstance(getBaseContext()).bindAppWidgetIdIfAllowed(id, info.provider); 83 setAppWidget(id); 84 } else { 85 startChooseActivity(); 86 } 87 } 88 89 @Override onStart()90 public void onStart() { 91 super.onStart(); 92 mAppWidgetHost.startListening(); 93 } 94 95 @Override onRetainNonConfigurationInstance()96 public Object onRetainNonConfigurationInstance() { 97 AppWidgetProviderInfo info = AppWidgetManager.getInstance( 98 getBaseContext()).getAppWidgetInfo(mAppWidgetId); 99 return info; 100 } 101 startChooseActivity()102 private void startChooseActivity() { 103 int id = mAppWidgetHost.allocateAppWidgetId(); 104 Intent selectIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK); 105 selectIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id); 106 startActivityForResult(selectIntent, REQUEST_WIDGET); 107 } 108 109 @Override onActivityResult(int requestCode, int resultCode, Intent data)110 public void onActivityResult(int requestCode, int resultCode, Intent data) { 111 if (requestCode == REQUEST_WIDGET) { 112 if (data != null) { 113 int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; 114 if (data.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) { 115 appWidgetId = data.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID); 116 } 117 118 if (resultCode == RESULT_OK) { 119 setAppWidget(appWidgetId); 120 } else { 121 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 122 finish(); 123 } 124 } else { 125 finish(); 126 } 127 } else if (requestCode == REQUEST_CONFIGURE) { 128 if (data != null) { 129 int appWidgetId = data.getExtras().getInt( 130 AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); 131 if (resultCode == RESULT_OK) { 132 finishSetAppWidget(appWidgetId); 133 } else { 134 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 135 } 136 } 137 } 138 } 139 setAppWidget(int appWidgetId)140 private void setAppWidget(int appWidgetId) { 141 if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { 142 mAppWidgetHost.deleteAppWidgetId(mAppWidgetId); 143 } 144 145 /* Check for configuration */ 146 AppWidgetProviderInfo providerInfo = 147 AppWidgetManager.getInstance(getBaseContext()).getAppWidgetInfo(appWidgetId); 148 149 if (providerInfo.configure != null) { 150 Intent configureIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); 151 configureIntent.setComponent(providerInfo.configure); 152 configureIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 153 154 if (configureIntent != null) { 155 try { 156 startActivityForResult(configureIntent, REQUEST_CONFIGURE); 157 } catch (ActivityNotFoundException e) { 158 Log.d(LOG_TAG, "Configuration activity not found: " + e); 159 Toast errorToast = Toast.makeText( 160 getBaseContext(), R.string.configure_error, Toast.LENGTH_SHORT); 161 errorToast.show(); 162 } 163 } 164 } else { 165 finishSetAppWidget(appWidgetId); 166 } 167 } 168 finishSetAppWidget(int appWidgetId)169 private void finishSetAppWidget(int appWidgetId) { 170 AppWidgetProviderInfo providerInfo = 171 AppWidgetManager.getInstance(getBaseContext()).getAppWidgetInfo(appWidgetId); 172 if (providerInfo != null) { 173 mAppWidgetView = 174 mAppWidgetHost.createView(getBaseContext(), appWidgetId, providerInfo); 175 176 int [] dimensions = 177 getLauncherCellDimensions(providerInfo.minWidth, providerInfo.minHeight); 178 179 mPreviewWidth = dimensions[0]; 180 mPreviewHeight = dimensions[1]; 181 182 mAppWidgetName = 183 AppWidgetManager.getInstance(getBaseContext()).getAppWidgetInfo(appWidgetId).label; 184 mAppWidgetName = mAppWidgetName.replaceAll("[^a-zA-Z0-9]", "_"); 185 186 ViewGroup.LayoutParams p = new ViewGroup.LayoutParams( 187 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 188 mAppWidgetView.setLayoutParams(p); 189 mAppWidgetFrame.removeAllViews(); 190 mAppWidgetHost.deleteAppWidgetId(mAppWidgetId); 191 mAppWidgetFrame.addView(mAppWidgetView, mPreviewWidth, mPreviewHeight); 192 mAppWidgetId = appWidgetId; 193 } 194 } 195 196 // Taken from CellLayout.java getLauncherCellDimensions(int width, int height)197 public int[] getLauncherCellDimensions(int width, int height) { 198 // Always assume we're working with the smallest span to make sure we 199 // reserve enough space in both orientations. 200 Resources resources = getResources(); 201 int cellWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width); 202 int cellHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height); 203 int widthGap = resources.getDimensionPixelSize(R.dimen.workspace_width_gap); 204 int heightGap = resources.getDimensionPixelSize(R.dimen.workspace_height_gap); 205 int previewCellSize = resources.getDimensionPixelSize(R.dimen.preview_cell_size); 206 207 // This logic imitates Launcher's CellLayout.rectToCell. 208 // Always round up to next largest cell 209 int smallerSize = Math.min(cellWidth, cellHeight); 210 int spanX = (width + smallerSize) / smallerSize; 211 int spanY = (height + smallerSize) / smallerSize; 212 213 // We use a fixed preview cell size so that you get the same preview image for 214 // the same cell-sized widgets across all devices 215 width = spanX * previewCellSize + ((spanX - 1) * widthGap); 216 height = spanY * previewCellSize + ((spanY - 1) * heightGap); 217 return new int[] { width, height }; 218 } 219 buildFile(String name)220 private File buildFile(String name) { 221 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 222 return null; 223 } 224 225 File path = Environment.getExternalStoragePublicDirectory( 226 Environment.DIRECTORY_DOWNLOADS); 227 int orientationCode = getResources().getConfiguration().orientation; 228 String orientation; 229 if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { 230 orientation = "landscape"; 231 } else if (orientationCode == Configuration.ORIENTATION_PORTRAIT) { 232 orientation = "portrait"; 233 } else if (orientationCode == Configuration.ORIENTATION_SQUARE) { 234 orientation = "square"; 235 } else { 236 orientation = "undefined"; 237 } 238 return new File(path, name + "_ori_" + orientation + ".png"); 239 } 240 getPreviewBitmap()241 public Bitmap getPreviewBitmap() { 242 mAppWidgetView.invalidate(); 243 Bitmap bmp = Bitmap.createBitmap( 244 mAppWidgetView.getWidth(), mAppWidgetView.getHeight(), Config.ARGB_8888); 245 Canvas c = new Canvas(bmp); 246 mAppWidgetView.draw(c); 247 return bmp; 248 } 249 saveImage(Bitmap bmp, String name)250 private boolean saveImage(Bitmap bmp, String name) { 251 File pic = buildFile(mAppWidgetName); 252 if (pic == null) { 253 Log.d(LOG_TAG, "External storage not present"); 254 return false; 255 } 256 257 pic.getParentFile().mkdirs(); 258 FileOutputStream fout = null; 259 try { 260 fout = new FileOutputStream(pic); 261 if (!bmp.compress(CompressFormat.PNG, 100, fout)) { 262 Log.d(LOG_TAG, "Failed to compress image"); 263 return false; 264 } 265 return true; 266 } catch (IOException e) { 267 Log.d(LOG_TAG, "Error writing to disk: " + e); 268 } finally { 269 try { 270 if (fout != null) { 271 fout.close(); 272 } 273 } catch (IOException e) { 274 Log.d(LOG_TAG, "Could not close file: " + e); 275 } 276 } 277 return false; 278 } 279 280 @Override onBackPressed()281 public void onBackPressed() { 282 if (!getFragmentManager().popBackStackImmediate()) { 283 startChooseActivity(); 284 } 285 } 286 287 @Override onClick(View v)288 public void onClick(View v) { 289 if (v == mSnapshotButton) { 290 int textId = R.string.saving_preview; 291 292 Toast preToast = Toast.makeText(getBaseContext(), textId, Toast.LENGTH_SHORT); 293 preToast.show(); 294 295 Bitmap bmp = getPreviewBitmap(); 296 if (saveImage(bmp, mAppWidgetName)) { 297 textId = R.string.preview_saved; 298 } else { 299 textId = R.string.preview_save_error; 300 } 301 302 Toast postToast = Toast.makeText(getBaseContext(), textId, Toast.LENGTH_SHORT); 303 postToast.show(); 304 } else if (v == mEmailButton) { 305 File file = buildFile(mAppWidgetName); 306 if (file.exists()) { 307 Intent emailIntent = new Intent(Intent.ACTION_SEND); 308 emailIntent.setType("image/png"); 309 emailIntent.putExtra(Intent.EXTRA_SUBJECT, 310 getResources().getString(R.string.email_subject)); 311 emailIntent.putExtra(Intent.EXTRA_TEXT, 312 getResources().getString(R.string.email_body)); 313 emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file)); 314 startActivity(emailIntent); 315 } else { 316 Toast postToast = Toast.makeText( 317 getBaseContext(), R.string.no_preview, Toast.LENGTH_SHORT); 318 postToast.show(); 319 } 320 } 321 } 322 } 323