package com.android.launcher2; import java.util.ArrayList; import java.util.List; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.android.launcher.R; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Parcelable; import android.util.Log; import android.widget.Toast; public class HomescreenConfigurationReceiver extends BroadcastReceiver { public static final String OPTIONS_TYPE = "type"; public static final String OPTIONS_TITLE = "title";//Not used public static final String OPTIONS_PACKAGE_NAME = "packageName"; public static final String OPTIONS_CLASS_NAME = "className"; public static final String OPTIONS_CONTAINER = "container"; public static final String OPTIONS_SCREEN = "screen"; public static final String OPTIONS_X = "x"; public static final String OPTIONS_Y = "y"; public static final String OPTIONS_ROWS = "rows"; public static final String OPTIONS_COLUMNS = "cols"; public static final String ITEM_TYPE_WIDGET = "widget"; public static final String ITEM_TYPE_SHORTCUT = "shortcut"; /** * Version of this API */ public static final String OPTIONS_VERSION = "version"; /** * The protocol version of this service is incorrect */ public static final String RESULT_INVALID_VERSION = "RESULT_INVALID_VERSION"; public static final String RESULT_MISSING_REQUIRED_PARAMETER = "RESULT_MISSING_REQUIRED_PARAMETER"; public static final String RESULT_INVALID_JSON = "RESULT_INVALID_JSON"; /** * Unknown email failure */ public static final String RESULT_UNKNOWN = "RESULT_UNKNOWN"; /** * Email account creation success */ public static final int RESULT_CODE_SUCCESS = 0x0; /** * Email account creation failure */ public static final int RESULT_CODE_FAILURE = 0x1; private static final Float SUPPORTED_VERSION = 1.0f;//TODO: supported version @Override public void onReceive(Context context, Intent intent) { Log.i("Homescreen", "starting configuration"); // if (!isVersionProtocolSupported(intent.getExtras())) { // sendResponse(context, null, false, RESULT_INVALID_VERSION); // return; // } List<Bundle> bundles = null; if (intent.hasExtra("homescreen")) { bundles = fromIntentToBundles(intent); if(bundles.isEmpty()) { sendResponse(context, null, false, RESULT_INVALID_JSON); } } else { sendResponse(context, null, false, RESULT_MISSING_REQUIRED_PARAMETER); return; } boolean hasError = false; JSONArray messages = new JSONArray(); for(Bundle options : bundles) { JSONObject payload = null; try { payload = new JSONObject(options.getString( "jsonObject" )); } catch ( JSONException e ) { e.printStackTrace(); } if (hasRequiredOptions(payload)) { int containerType = options.getInt(OPTIONS_CONTAINER) > 0 ? LauncherSettings.Favorites.CONTAINER_DESKTOP : LauncherSettings.Favorites.CONTAINER_HOTSEAT; String itemType = null; try { itemType = payload.getString( "type" ); } catch ( JSONException e1 ) { e1.printStackTrace(); } ComponentName cn = new ComponentName(options.getString(OPTIONS_PACKAGE_NAME) , options.getString(OPTIONS_CLASS_NAME)); if (itemType.equals( ITEM_TYPE_WIDGET ) ) { installWidget(context, cn, options.getInt(OPTIONS_SCREEN, -1), containerType, options.getInt(OPTIONS_X, -1), options.getInt(OPTIONS_Y, -1), options.getInt(OPTIONS_ROWS, -1), options.getInt(OPTIONS_COLUMNS, -1), true); } else if (itemType.equals( ITEM_TYPE_SHORTCUT ) ) { installShortCut( context, cn, options.getInt(OPTIONS_SCREEN, -1), containerType, options.getInt(OPTIONS_X, -1), options.getInt(OPTIONS_Y, -1) ); } messages.put(buildResponse(options, true, 0, "OK")); } else { hasError = true; messages.put(buildResponse(options, true, 0, RESULT_MISSING_REQUIRED_PARAMETER)); } } sendResponse(context, messages, hasError, null); } private void sendResponse(Context context, JSONArray items, boolean isSuccess, String errorMessage) { Intent result = new Intent( "com.android.homescreen.CONFIGURE_HOMESCREEN_RESULT"); result.putExtra("success", isSuccess); result.putExtra("version", "1.0");//TODO - hard-coded result.putExtra("intent", "com.android.homescreen.CONFIGURE_HOMESCREEN"); if (items != null) { Log.d("Homescreen", items.toString()); result.putExtra("homescreen", items.toString()); } else { result.putExtra("errorCode", 0); if(errorMessage != null) { result.putExtra("errorMessage", errorMessage); Log.i("Homescreen", errorMessage); } } context.sendBroadcast(result); } private JSONObject buildResponse(Bundle options, boolean isSuccess, int errorCode, String errorMessage) { try { JSONObject payload = new JSONObject(options.getString("jsonObject")); payload.put("success", (isSuccess ? RESULT_CODE_SUCCESS : RESULT_CODE_FAILURE) ); payload.put("errorCode", errorCode); payload.put("errorMessage", errorMessage); return payload; } catch (JSONException e) { e.printStackTrace(); } return null; } private boolean hasRequiredOptions(JSONObject options) { boolean hasOptions = options.has(OPTIONS_TYPE) && options.has(OPTIONS_PACKAGE_NAME) && options.has(OPTIONS_CLASS_NAME) && options.has(OPTIONS_CONTAINER); try { if(hasOptions && options.getInt(OPTIONS_CONTAINER) > 0 ) { return options.has(OPTIONS_SCREEN); } } catch ( JSONException e ) { e.printStackTrace(); } return hasOptions; } private List<Bundle> fromIntentToBundles(Intent intent) { try { JSONArray bundledArray = new JSONArray( intent.getStringExtra("homescreen")); Log.d("fromIntentToBundles", "bundledArray = " + bundledArray.toString()); // TODO: better to use homescreens if (bundledArray != null) { return fromJsonToBundles(bundledArray); } } catch (JSONException e) { e.printStackTrace(); } return new ArrayList<Bundle>(); } private List<Bundle> fromJsonToBundles(JSONArray items) { ArrayList<Bundle> bundles = new ArrayList<Bundle>(); try { for ( int index = 0; index < items.length(); index++ ) { JSONObject item = items.getJSONObject( index ); Bundle bundle = new Bundle(); bundle.putString("jsonObject", item.toString()); addOptionString(OPTIONS_TYPE, item, bundle); addOptionString(OPTIONS_PACKAGE_NAME, item, bundle); addOptionString(OPTIONS_CLASS_NAME, item, bundle); addOptionInt(OPTIONS_CONTAINER, item, bundle); addOptionInt(OPTIONS_SCREEN, item, bundle); addOptionInt(OPTIONS_X, item, bundle); addOptionInt(OPTIONS_Y, item, bundle); addOptionInt(OPTIONS_ROWS, item, bundle); addOptionInt(OPTIONS_COLUMNS, item, bundle); bundles.add(bundle); } } catch ( JSONException je ) { je.printStackTrace(); } Log.d("Homescreen", "fromJsonToBundles.bundles = " + bundles.toString()); return bundles; } private void addOptionString(String option, JSONObject item, Bundle bundle ) { if(!item.isNull(option)) { try { bundle.putString(option, item.getString(option)); } catch (JSONException e) { } } } private void addOptionInt(String option, JSONObject item, Bundle bundle ) { if(!item.isNull(option)) { try { bundle.putInt(option, item.getInt(option)); } catch (JSONException e) { } } } private LauncherAppWidgetInfo installWidget(Context context, ComponentName cn, int screen, int container, int xCoOd, int yCoOd, int spanX, int spanY, boolean notify) { LauncherApplication app = (LauncherApplication) context .getApplicationContext(); return app.getModel().addAppWidget(context, cn, container, screen, xCoOd, yCoOd, spanX, spanY, true); } /** * Returns true if version protocol is supported, otherwise false * * @param options * email account options * @return true if version protocol is supported, otherwise false */ private static boolean isVersionProtocolSupported(Bundle options) { // TODO: Wouldn't this be better as a float: options.getFloat? try { Float version = Float.valueOf(options.getString(OPTIONS_VERSION, "0.0")); return !version.equals(0.0f) && (version <= SUPPORTED_VERSION); } catch (NumberFormatException e) { return false; } } private boolean installShortCut(Context context, ComponentName cn, int screen, int container, int xCoOd, int yCoOd) { String name = getAppName(context, cn.getPackageName()); Drawable icon = getAppIcon(context, cn); BitmapDrawable bd = (BitmapDrawable) icon; Intent shortcutIntent = new Intent(); shortcutIntent.setClassName(cn.getPackageName(), cn.getClassName()); shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); Intent data = new Intent(); data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); data.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); data.putExtra(Intent.EXTRA_SHORTCUT_ICON, bd.getBitmap()); if (findEmptyCell(context, new int[] {xCoOd, yCoOd}, screen)) { if (shortcutIntent != null) { if (shortcutIntent.getAction() == null) { shortcutIntent.setAction(Intent.ACTION_VIEW); } // By default, we allow for duplicate entries (located in // different places) boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true); if ( duplicate || !LauncherModel.shortcutExists(context, name, shortcutIntent)) { LauncherApplication app = (LauncherApplication) context.getApplicationContext(); ShortcutInfo info; if (xCoOd == -1 && yCoOd == -1) { info = app.getModel().addShortcut(context, data, LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, xCoOd, yCoOd, true); } else { info = app.getModel().addShortcut(context, data, LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, xCoOd, yCoOd, true); } if (info != null) { Toast.makeText(context, context.getString(R.string.shortcut_installed, name), Toast.LENGTH_SHORT).show(); } else { return false; } } else { Toast.makeText(context, context.getString(R.string.shortcut_duplicate, name), Toast.LENGTH_SHORT).show(); } return true; } } else { Toast.makeText(context, context.getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); } return false; } private static boolean findEmptyCell(Context context, int[] xy, int screen) { final int xCount = LauncherModel.getCellCountX(); final int yCount = LauncherModel.getCellCountY(); boolean[][] occupied = new boolean[xCount][yCount]; ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context); ItemInfo item = null; int cellX, cellY, spanX, spanY; for (int i = 0; i < items.size(); ++i) { item = items.get(i); if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { if (item.screen == screen) { cellX = item.cellX; cellY = item.cellY; spanX = item.spanX; spanY = item.spanY; for (int x = cellX; x < cellX + spanX && x < xCount; x++) { for (int y = cellY; y < cellY + spanY && y < yCount; y++) { occupied[x][y] = true; } } } } } return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied); } private String getAppName(Context context, String packageName) { final PackageManager pm = context.getPackageManager(); android.content.pm.ApplicationInfo ai; try { ai = pm.getApplicationInfo(packageName, 0); } catch (final NameNotFoundException e) { ai = null; } return ( String ) ( ai != null ? pm.getApplicationLabel(ai) : "(unknown)" ); } private Drawable getAppIcon(Context context, ComponentName cn) { final PackageManager pm = context.getPackageManager(); try { return pm.getActivityIcon(cn); } catch ( NameNotFoundException e ) { e.printStackTrace(); return null; } } }