From 41669fc9c6d4faab98d527c8f4c449725c29c8df Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Thu, 13 Aug 2009 12:53:24 -0700 Subject: [PATCH] Add live wallpapers picker to Launcher2. This is temporary. This change requires changing the signature and shared user id of Launcher2, we will have to revert this once we move the wallpapers picker back to Settings. --- Android.mk | 18 +- AndroidManifest.xml | 16 +- res/layout/pick_item.xml | 25 ++ res/values/strings.xml | 4 + src/com/android/launcher2/ActivityPicker.java | 405 ++++++++++++++++++ .../launcher2/LiveWallpaperPickActivity.java | 116 +++++ 6 files changed, 582 insertions(+), 2 deletions(-) create mode 100644 res/layout/pick_item.xml create mode 100644 src/com/android/launcher2/ActivityPicker.java create mode 100644 src/com/android/launcher2/LiveWallpaperPickActivity.java diff --git a/Android.mk b/Android.mk index 73ef0352a0..062ebf5489 100644 --- a/Android.mk +++ b/Android.mk @@ -14,6 +14,21 @@ # limitations under the License. # +###################################################### +# +# WARNING +# +# /\ +# /||\ +# / || \ +# / \ +# / || \ +# /__________\ +# +# Change the LOCAL_CERTIFICATE back to shared +# +###################################################### + LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) @@ -22,7 +37,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := Launcher2 -LOCAL_CERTIFICATE := shared +LOCAL_CERTIFICATE := platform +#LOCAL_CERTIFICATE := shared #LOCAL_OVERRIDES_PACKAGES := Home diff --git a/AndroidManifest.xml b/AndroidManifest.xml index cdfe585ab3..701ca77d8b 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -17,10 +17,11 @@ ** limitations under the License. */ --> + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index f286fa11ec..476f47ab40 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -34,6 +34,9 @@ Wallpaper gallery Application is not installed on your phone. + + Live wallpaper @@ -126,4 +129,5 @@ Problem loading widget + diff --git a/src/com/android/launcher2/ActivityPicker.java b/src/com/android/launcher2/ActivityPicker.java new file mode 100644 index 0000000000..f502e27281 --- /dev/null +++ b/src/com/android/launcher2/ActivityPicker.java @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher2; + +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.Intent.ShortcutIconResource; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.PaintDrawable; +import android.os.Bundle; +import android.os.Parcelable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Displays a list of all activities matching the incoming + * {@link android.content.Intent#EXTRA_INTENT} query, along with any injected items. + */ +public class ActivityPicker extends AlertActivity implements + DialogInterface.OnClickListener, DialogInterface.OnCancelListener { + + /** + * Adapter of items that are displayed in this dialog. + */ + private PickAdapter mAdapter; + + /** + * Base {@link android.content.Intent} used when building list. + */ + private Intent mBaseIntent; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Intent intent = getIntent(); + + // Read base intent from extras, otherwise assume default + Parcelable parcel = intent.getParcelableExtra(Intent.EXTRA_INTENT); + if (parcel instanceof Intent) { + mBaseIntent = (Intent) parcel; + } else { + mBaseIntent = new Intent(Intent.ACTION_MAIN, null); + mBaseIntent.addCategory(Intent.CATEGORY_DEFAULT); + } + + // Create dialog parameters + AlertController.AlertParams params = mAlertParams; + params.mOnClickListener = this; + params.mOnCancelListener = this; + + // Use custom title if provided, otherwise default window title + if (intent.hasExtra(Intent.EXTRA_TITLE)) { + params.mTitle = intent.getStringExtra(Intent.EXTRA_TITLE); + } else { + params.mTitle = getTitle(); + } + + // Build list adapter of pickable items + List items = getItems(); + mAdapter = new PickAdapter(this, items); + params.mAdapter = mAdapter; + + setupAlert(); + } + + /** + * Handle clicking of dialog item by passing back + * {@link #getIntentForPosition(int)} in {@link #setResult(int, android.content.Intent)}. + */ + public void onClick(DialogInterface dialog, int which) { + Intent intent = getIntentForPosition(which); + setResult(Activity.RESULT_OK, intent); + finish(); + } + + /** + * Handle canceled dialog by passing back {@link android.app.Activity#RESULT_CANCELED}. + */ + public void onCancel(DialogInterface dialog) { + setResult(Activity.RESULT_CANCELED); + finish(); + } + + /** + * Build the specific {@link android.content.Intent} for a given list position. Convenience + * method that calls through to {@link PickAdapter.Item#getIntent(android.content.Intent)}. + */ + protected Intent getIntentForPosition(int position) { + PickAdapter.Item item = (PickAdapter.Item) mAdapter.getItem(position); + return item.getIntent(mBaseIntent); + } + + /** + * Build and return list of items to be shown in dialog. Default + * implementation mixes activities matching {@link #mBaseIntent} from + * {@link #putIntentItems(android.content.Intent, java.util.List)} with any injected items from + * {@link android.content.Intent#EXTRA_SHORTCUT_NAME}. Override this method in subclasses to + * change the items shown. + */ + protected List getItems() { + PackageManager packageManager = getPackageManager(); + List items = new ArrayList(); + + // Add any injected pick items + final Intent intent = getIntent(); + ArrayList labels = + intent.getStringArrayListExtra(Intent.EXTRA_SHORTCUT_NAME); + ArrayList icons = + intent.getParcelableArrayListExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); + + if (labels != null && icons != null && labels.size() == icons.size()) { + for (int i = 0; i < labels.size(); i++) { + String label = labels.get(i); + Drawable icon = null; + + try { + // Try loading icon from requested package + ShortcutIconResource iconResource = icons.get(i); + Resources res = packageManager.getResourcesForApplication( + iconResource.packageName); + icon = res.getDrawable(res.getIdentifier( + iconResource.resourceName, null, null)); + } catch (NameNotFoundException e) { + // Ignore + } + + items.add(new PickAdapter.Item(this, label, icon)); + } + } + + // Add any intent items if base was given + if (mBaseIntent != null) { + putIntentItems(mBaseIntent, items); + } + + return items; + } + + /** + * Fill the given list with any activities matching the base {@link android.content.Intent}. + */ + protected void putIntentItems(Intent baseIntent, List items) { + PackageManager packageManager = getPackageManager(); + List list = packageManager.queryIntentActivities(baseIntent, + 0 /* no flags */); + Collections.sort(list, new ResolveInfo.DisplayNameComparator(packageManager)); + + final int listSize = list.size(); + for (int i = 0; i < listSize; i++) { + ResolveInfo resolveInfo = list.get(i); + items.add(new PickAdapter.Item(this, packageManager, resolveInfo)); + } + } + + /** + * Adapter which shows the set of activities that can be performed for a + * given {@link android.content.Intent}. + */ + protected static class PickAdapter extends BaseAdapter { + + /** + * Item that appears in a {@link PickAdapter} list. + */ + public static class Item { + protected static IconResizer sResizer; + + protected IconResizer getResizer(Context context) { + if (sResizer == null) { + sResizer = new IconResizer(context); + } + return sResizer; + } + + CharSequence label; + Drawable icon; + String packageName; + String className; + Bundle extras; + + /** + * Create a list item from given label and icon. + */ + Item(Context context, CharSequence label, Drawable icon) { + this.label = label; + this.icon = getResizer(context).createIconThumbnail(icon); + } + + /** + * Create a list item and fill it with details from the given + * {@link android.content.pm.ResolveInfo} object. + */ + Item(Context context, PackageManager pm, ResolveInfo resolveInfo) { + label = resolveInfo.loadLabel(pm); + if (label == null && resolveInfo.activityInfo != null) { + label = resolveInfo.activityInfo.name; + } + + icon = getResizer(context).createIconThumbnail(resolveInfo.loadIcon(pm)); + packageName = resolveInfo.activityInfo.applicationInfo.packageName; + className = resolveInfo.activityInfo.name; + } + + Intent getIntent(Intent baseIntent) { + Intent intent = new Intent(baseIntent); + if (packageName != null && className != null) { + // Valid package and class, so fill details as normal intent + intent.setClassName(packageName, className); + if (extras != null) { + intent.putExtras(extras); + } + } else { + // No valid package or class, so treat as shortcut with label + intent.setAction(Intent.ACTION_CREATE_SHORTCUT); + intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, label); + } + return intent; + } + } + + private final LayoutInflater mInflater; + + private List mItems; + private int mLayoutRes = R.layout.pick_item; + + /** + * Create an adapter for the given items. + */ + public PickAdapter(Context context, List items) { + mInflater = (LayoutInflater) + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mItems = items; + } + + /** + * {@inheritDoc} + */ + public int getCount() { + return mItems.size(); + } + + /** + * {@inheritDoc} + */ + public Object getItem(int position) { + return mItems.get(position); + } + + /** + * {@inheritDoc} + */ + public long getItemId(int position) { + return position; + } + + /** + * {@inheritDoc} + */ + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = mInflater.inflate(mLayoutRes, parent, false); + } + + Item item = (Item) getItem(position); + TextView textView = (TextView) convertView; + textView.setText(item.label); + textView.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null); + + return convertView; + } + } + + /** + * Utility class to resize icons to match default icon size. Code is mostly + * borrowed from Launcher. + */ + private static class IconResizer { + private int mIconWidth = -1; + private int mIconHeight = -1; + + private final Rect mOldBounds = new Rect(); + private Canvas mCanvas = new Canvas(); + private Resources mResources; + + public IconResizer(Context context) { + mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, + Paint.FILTER_BITMAP_FLAG)); + + mResources = context.getResources(); + mIconWidth = mIconHeight = (int) mResources.getDimension( + android.R.dimen.app_icon_size); + } + + /** + * Returns a Drawable representing the thumbnail of the specified Drawable. + * The size of the thumbnail is defined by the dimension + * android.R.dimen.launcher_application_icon_size. + * + * This method is not thread-safe and should be invoked on the UI thread only. + * + * @param icon The icon to get a thumbnail of. + * + * @return A thumbnail for the specified icon or the icon itself if the + * thumbnail could not be created. + */ + public Drawable createIconThumbnail(Drawable icon) { + int width = mIconWidth; + int height = mIconHeight; + + if (icon == null) { + return null; + } + + final int iconWidth = icon.getIntrinsicWidth(); + final int iconHeight = icon.getIntrinsicHeight(); + + if (icon instanceof PaintDrawable) { + PaintDrawable painter = (PaintDrawable) icon; + painter.setIntrinsicWidth(width); + painter.setIntrinsicHeight(height); + } + + if (width > 0 && height > 0) { + if (width < iconWidth || height < iconHeight) { + final float ratio = (float) iconWidth / iconHeight; + + if (iconWidth > iconHeight) { + height = (int) (width / ratio); + } else if (iconHeight > iconWidth) { + width = (int) (height * ratio); + } + + final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ? + Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); + final Canvas canvas = mCanvas; + canvas.setBitmap(thumb); + // Copy the old bounds to restore them later + // If we were to do oldBounds = icon.getBounds(), + // the call to setBounds() that follows would + // change the same instance and we would lose the + // old bounds + mOldBounds.set(icon.getBounds()); + final int x = (mIconWidth - width) / 2; + final int y = (mIconHeight - height) / 2; + icon.setBounds(x, y, x + width, y + height); + icon.draw(canvas); + icon.setBounds(mOldBounds); + icon = new BitmapDrawable(mResources, thumb); + } else if (iconWidth < width && iconHeight < height) { + final Bitmap.Config c = Bitmap.Config.ARGB_8888; + final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); + final Canvas canvas = mCanvas; + canvas.setBitmap(thumb); + mOldBounds.set(icon.getBounds()); + final int x = (width - iconWidth) / 2; + final int y = (height - iconHeight) / 2; + icon.setBounds(x, y, x + iconWidth, y + iconHeight); + icon.draw(canvas); + icon.setBounds(mOldBounds); + icon = new BitmapDrawable(mResources, thumb); + } + } + + return icon; + } + } +} \ No newline at end of file diff --git a/src/com/android/launcher2/LiveWallpaperPickActivity.java b/src/com/android/launcher2/LiveWallpaperPickActivity.java new file mode 100644 index 0000000000..6119a8af04 --- /dev/null +++ b/src/com/android/launcher2/LiveWallpaperPickActivity.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher2; + +import android.app.WallpaperManager; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.RemoteException; +import android.service.wallpaper.WallpaperService; +import android.util.Log; + +import java.text.Collator; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +/** + * Displays a list of live wallpapers, allowing the user to select one + * and make it the system global wallpaper. + */ +public class LiveWallpaperPickActivity extends ActivityPicker { + private static final String TAG = "LiveWallpaperPickActivity"; + + private PackageManager mPackageManager; + private WallpaperManager mWallpaperManager; + + @Override + public void onCreate(Bundle icicle) { + mPackageManager = getPackageManager(); + mWallpaperManager = WallpaperManager.getInstance(this); + + super.onCreate(icicle); + + // Set default return data + setResult(RESULT_CANCELED); + } + + /** + * {@inheritDoc} + */ + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = getIntentForPosition(which); + try { + mWallpaperManager.getIWallpaperManager().setWallpaperComponent( + intent.getComponent()); + this.setResult(RESULT_OK); + } catch (RemoteException e) { + // do nothing + } catch (RuntimeException e) { + Log.w(TAG, "Failure setting wallpaper", e); + } + finish(); + } + + void putLiveWallpaperItems(List ris, + List items) { + final int size = ris.size(); + for (int i = 0; i < size; i++) { + ServiceInfo si = ris.get(i).serviceInfo; + + CharSequence label = si.loadLabel(mPackageManager); + Drawable icon = si.loadIcon(mPackageManager); + + PickAdapter.Item item = new PickAdapter.Item(this, label, icon); + + item.packageName = si.packageName; + item.className = si.name; + + items.add(item); + } + } + + @Override + protected List getItems() { + List items = new ArrayList(); + + putInstalledLiveWallpapers(items); + + // Sort all items together by label + Collections.sort(items, new Comparator() { + Collator mCollator = Collator.getInstance(); + public int compare(PickAdapter.Item lhs, PickAdapter.Item rhs) { + return mCollator.compare(lhs.label, rhs.label); + } + }); + + return items; + } + + void putInstalledLiveWallpapers(List items) { + List ris = mPackageManager.queryIntentServices( + new Intent(WallpaperService.SERVICE_INTERFACE), 0); + putLiveWallpaperItems(ris, items); + } +}