diff options
author | Michael Jurka <mikejurka@google.com> | 2013-07-08 18:03:46 -0700 |
---|---|---|
committer | Michael Jurka <mikejurka@google.com> | 2013-08-21 13:18:29 +0200 |
commit | 104c45677660586026a7e74ef8c47d396403d50e (patch) | |
tree | f9a2a66390820d69963ec626990d4bc0e58a7c02 /src/com/android/launcher3 | |
parent | 912d360cd7350d8fe959202c6b5d0a5aa97790aa (diff) | |
download | android_packages_apps_Trebuchet-104c45677660586026a7e74ef8c47d396403d50e.tar.gz android_packages_apps_Trebuchet-104c45677660586026a7e74ef8c47d396403d50e.tar.bz2 android_packages_apps_Trebuchet-104c45677660586026a7e74ef8c47d396403d50e.zip |
Initial implementation of new wallpaper picker.
Change-Id: Ib4c5ac4989b4959fa62465d9cde3cac662e24949
Diffstat (limited to 'src/com/android/launcher3')
-rw-r--r-- | src/com/android/launcher3/CropView.java | 168 | ||||
-rw-r--r-- | src/com/android/launcher3/Launcher.java | 20 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherAppState.java | 8 | ||||
-rw-r--r-- | src/com/android/launcher3/PreloadReceiver.java | 3 | ||||
-rw-r--r-- | src/com/android/launcher3/WallpaperCropActivity.java | 289 | ||||
-rw-r--r-- | src/com/android/launcher3/WallpaperPickerActivity.java | 441 | ||||
-rw-r--r-- | src/com/android/launcher3/Workspace.java | 61 |
7 files changed, 915 insertions, 75 deletions
diff --git a/src/com/android/launcher3/CropView.java b/src/com/android/launcher3/CropView.java new file mode 100644 index 000000000..5b4928298 --- /dev/null +++ b/src/com/android/launcher3/CropView.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2013 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.launcher3; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.ScaleGestureDetector.OnScaleGestureListener; + +import com.android.photos.views.TiledImageRenderer.TileSource; +import com.android.photos.views.TiledImageView; + +public class CropView extends TiledImageView implements OnScaleGestureListener { + + private ScaleGestureDetector mScaleGestureDetector; + private float mLastX, mLastY; + private float mMinScale; + + public CropView(Context context) { + this(context, null); + } + + public CropView(Context context, AttributeSet attrs) { + super(context, attrs); + mScaleGestureDetector = new ScaleGestureDetector(context, this); + } + + public RectF getCrop() { + final float width = getWidth(); + final float height = getHeight(); + final float imageWidth = mRenderer.source.getImageWidth(); + final float imageHeight = mRenderer.source.getImageHeight(); + final float scale = mRenderer.scale; + float centerX = (width / 2f - mRenderer.centerX + (imageWidth - width) / 2f) + * scale + width / 2f; + float centerY = (height / 2f - mRenderer.centerY + (imageHeight - height) / 2f) + * scale + height / 2f; + float leftEdge = centerX - imageWidth / 2f * scale; + float topEdge = centerY - imageHeight / 2f * scale; + + float cropLeft = -leftEdge / scale; + float cropTop = -topEdge / scale; + float cropRight = cropLeft + width / scale; + float cropBottom = cropTop + height / scale; + RectF cropRect = new RectF(cropLeft, cropTop, cropRight, cropBottom); + + return new RectF(cropLeft, cropTop, cropRight, cropBottom); + } + + public void setTileSource(TileSource source, Runnable isReadyCallback) { + super.setTileSource(source, isReadyCallback); + updateMinScale(getWidth(), getHeight(), source); + } + + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + updateMinScale(w, h, mRenderer.source); + } + + private void updateMinScale(int w, int h, TileSource source) { + synchronized (mLock) { + if (source != null) { + mMinScale = Math.max(w / (float) source.getImageWidth(), + h / (float) source.getImageHeight()); + mRenderer.scale = Math.max(mMinScale, mRenderer.scale); + } + } + } + + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + return true; + } + + @Override + public boolean onScale(ScaleGestureDetector detector) { + // Don't need the lock because this will only fire inside of + // onTouchEvent + mRenderer.scale *= detector.getScaleFactor(); + mRenderer.scale = Math.max(mMinScale, mRenderer.scale); + invalidate(); + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int action = event.getActionMasked(); + final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; + final int skipIndex = pointerUp ? event.getActionIndex() : -1; + + // Determine focal point + float sumX = 0, sumY = 0; + final int count = event.getPointerCount(); + for (int i = 0; i < count; i++) { + if (skipIndex == i) + continue; + sumX += event.getX(i); + sumY += event.getY(i); + } + final int div = pointerUp ? count - 1 : count; + float x = sumX / div; + float y = sumY / div; + + synchronized (mLock) { + mScaleGestureDetector.onTouchEvent(event); + switch (action) { + case MotionEvent.ACTION_MOVE: + mRenderer.centerX += (mLastX - x) / mRenderer.scale; + mRenderer.centerY += (mLastY - y) / mRenderer.scale; + invalidate(); + break; + } + if (mRenderer.source != null) { + // Adjust position so that the wallpaper covers the entire area + // of the screen + final float width = getWidth(); + final float height = getHeight(); + final float imageWidth = mRenderer.source.getImageWidth(); + final float imageHeight = mRenderer.source.getImageHeight(); + final float scale = mRenderer.scale; + float centerX = (width / 2f - mRenderer.centerX + (imageWidth - width) / 2f) + * scale + width / 2f; + float centerY = (height / 2f - mRenderer.centerY + (imageHeight - height) / 2f) + * scale + height / 2f; + float leftEdge = centerX - imageWidth / 2f * scale; + float rightEdge = centerX + imageWidth / 2f * scale; + float topEdge = centerY - imageHeight / 2f * scale; + float bottomEdge = centerY + imageHeight / 2f * scale; + if (leftEdge > 0) { + mRenderer.centerX += Math.ceil(leftEdge / scale); + } + if (rightEdge < getWidth()) { + mRenderer.centerX += (rightEdge - getWidth()) / scale; + } + if (topEdge > 0) { + mRenderer.centerY += Math.ceil(topEdge / scale); + } + if (bottomEdge < getHeight()) { + mRenderer.centerY += (bottomEdge - getHeight()) / scale; + } + } + } + + mLastX = x; + mLastY = y; + return true; + } +} diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 9ffc572eb..a16a33e58 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2040,20 +2040,9 @@ public class Launcher extends Activity private void startWallpaper() { showWorkspace(true); final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); - Intent chooser = Intent.createChooser(pickWallpaper, - getText(R.string.chooser_wallpaper)); - // NOTE: Adds a configure option to the chooser if the wallpaper supports it - // Removed in Eclair MR1 -// WallpaperManager wm = (WallpaperManager) -// getSystemService(Context.WALLPAPER_SERVICE); -// WallpaperInfo wi = wm.getWallpaperInfo(); -// if (wi != null && wi.getSettingsActivity() != null) { -// LabeledIntent li = new LabeledIntent(getPackageName(), -// R.string.configure_wallpaper, 0); -// li.setClassName(wi.getPackageName(), wi.getSettingsActivity()); -// chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li }); -// } - startActivityForResult(chooser, REQUEST_PICK_WALLPAPER); + pickWallpaper.setComponent( + new ComponentName(getPackageName(), WallpaperPickerActivity.class.getName())); + startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER); } /** @@ -4123,6 +4112,9 @@ public class Launcher extends Activity return null; } } + protected SharedPreferences getSharedPrefs() { + return mSharedPrefs; + } public boolean isFolderClingVisible() { Cling cling = (Cling) findViewById(R.id.folder_cling); if (cling != null) { diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 0c577e548..53d2ec50f 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.app.SearchManager; import android.content.*; import android.content.res.Configuration; +import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; import android.provider.Settings; @@ -76,7 +77,7 @@ public class LauncherAppState { } // set sIsScreenXLarge and mScreenDensity *before* creating icon cache - mIsScreenLarge = sContext.getResources().getBoolean(R.bool.is_large_tablet); + mIsScreenLarge = isScreenLarge(sContext.getResources()); mScreenDensity = sContext.getResources().getDisplayMetrics().density; mWidgetPreviewCacheDb = new WidgetPreviewLoader.CacheDb(sContext); @@ -188,6 +189,11 @@ public class LauncherAppState { return mIsScreenLarge; } + // Need a version that doesn't require an instance of LauncherAppState for the wallpaper picker + public static boolean isScreenLarge(Resources res) { + return res.getBoolean(R.bool.is_large_tablet); + } + public static boolean isScreenLandscape(Context context) { return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; diff --git a/src/com/android/launcher3/PreloadReceiver.java b/src/com/android/launcher3/PreloadReceiver.java index 4c9032f84..75e5c98bc 100644 --- a/src/com/android/launcher3/PreloadReceiver.java +++ b/src/com/android/launcher3/PreloadReceiver.java @@ -31,8 +31,7 @@ public class PreloadReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - final LauncherAppState app = LauncherAppState.getInstance(); - final LauncherProvider provider = app.getLauncherProvider(); + final LauncherProvider provider = LauncherAppState.getLauncherProvider(); if (provider != null) { String name = intent.getStringExtra(EXTRA_WORKSPACE_NAME); final int workspaceResId = !TextUtils.isEmpty(name) diff --git a/src/com/android/launcher3/WallpaperCropActivity.java b/src/com/android/launcher3/WallpaperCropActivity.java new file mode 100644 index 000000000..087785ef5 --- /dev/null +++ b/src/com/android/launcher3/WallpaperCropActivity.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2013 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.launcher3; + +import android.app.Activity; +import android.app.WallpaperManager; +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.Bitmap.CompressFormat; +import android.graphics.BitmapFactory; +import android.graphics.BitmapRegionDecoder; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.net.Uri; +import android.os.AsyncTask; +import android.util.Log; + +import com.android.gallery3d.common.Utils; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +// LAUNCHER crop activity! +public class WallpaperCropActivity extends Activity { + private static final String LOGTAG = "Launcher3.CropActivity"; + + private int mOutputX = 0; + private int mOutputY = 0; + + protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width"; + protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height"; + private static final int DEFAULT_COMPRESS_QUALITY = 90; + /** + * The maximum bitmap size we allow to be returned through the intent. + * Intents have a maximum of 1MB in total size. However, the Bitmap seems to + * have some overhead to hit so that we go way below the limit here to make + * sure the intent stays below 1MB.We should consider just returning a byte + * array instead of a Bitmap instance to avoid overhead. + */ + public static final int MAX_BMAP_IN_INTENT = 750000; + + + protected class BitmapCropTask extends AsyncTask<Void, Void, Boolean> { + Uri mInUri = null; + InputStream mInStream; + RectF mCropBounds = null; + int mOutWidth, mOutHeight; + int mRotation = 0; // for now + protected final WallpaperManager mWPManager; + String mOutputFormat = "jpg"; // for now + boolean mSetWallpaper; + boolean mSaveCroppedBitmap; + Bitmap mCroppedBitmap; + + public BitmapCropTask(Uri inUri, RectF cropBounds, int outWidth, int outHeight, + boolean setWallpaper, boolean saveCroppedBitmap) { + mInUri = inUri; + mCropBounds = cropBounds; + mOutWidth = outWidth; + mOutHeight = outHeight; + mWPManager = WallpaperManager.getInstance(getApplicationContext()); + mSetWallpaper = setWallpaper; + mSaveCroppedBitmap = saveCroppedBitmap; + } + + // Helper to setup input stream + private void regenerateInputStream() { + if (mInUri == null) { + Log.w(LOGTAG, "cannot read original file, no input URI given"); + } else { + Utils.closeSilently(mInStream); + try { + mInStream = new BufferedInputStream( + getContentResolver().openInputStream(mInUri)); + } catch (FileNotFoundException e) { + Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); + } + } + } + + public Point getImageBounds() { + regenerateInputStream(); + if (mInStream != null) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(mInStream, null, options); + if (options.outWidth != 0 && options.outHeight != 0) { + return new Point(options.outWidth, options.outHeight); + } + } + return null; + } + + public void setCropBounds(RectF cropBounds) { + mCropBounds = cropBounds; + } + + public Bitmap getCroppedBitmap() { + return mCroppedBitmap; + } + public boolean cropBitmap() { + boolean failure = false; + + regenerateInputStream(); + + if (mInStream != null) { + // Find crop bounds (scaled to original image size) + Rect roundedTrueCrop = new Rect(); + mCropBounds.roundOut(roundedTrueCrop); + + if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { + Log.w(LOGTAG, "crop has bad values for full size image"); + failure = true; + return false; + } + + // See how much we're reducing the size of the image + int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / mOutWidth, + roundedTrueCrop.height() / mOutHeight); + + // Attempt to open a region decoder + BitmapRegionDecoder decoder = null; + try { + decoder = BitmapRegionDecoder.newInstance(mInStream, true); + } catch (IOException e) { + Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); + } + + Bitmap crop = null; + if (decoder != null) { + // Do region decoding to get crop bitmap + BitmapFactory.Options options = new BitmapFactory.Options(); + if (scaleDownSampleSize > 1) { + options.inSampleSize = scaleDownSampleSize; + } + crop = decoder.decodeRegion(roundedTrueCrop, options); + decoder.recycle(); + } + + if (crop == null) { + // BitmapRegionDecoder has failed, try to crop in-memory + regenerateInputStream(); + Bitmap fullSize = null; + if (mInStream != null) { + BitmapFactory.Options options = new BitmapFactory.Options(); + if (scaleDownSampleSize > 1) { + options.inSampleSize = scaleDownSampleSize; + } + fullSize = BitmapFactory.decodeStream(mInStream, null, options); + } + if (fullSize != null) { + crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, + roundedTrueCrop.top, roundedTrueCrop.width(), + roundedTrueCrop.height()); + } + } + + if (crop == null) { + Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); + failure = true; + return false; + } + if (mOutputX > 0 && mOutputY > 0) { + Matrix m = new Matrix(); + RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); + if (mRotation > 0) { + m.setRotate(mRotation); + m.mapRect(cropRect); + } + RectF returnRect = new RectF(0, 0, mOutputX, mOutputY); + m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); + m.preRotate(mRotation); + Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), + (int) returnRect.height(), Bitmap.Config.ARGB_8888); + if (tmp != null) { + Canvas c = new Canvas(tmp); + c.drawBitmap(crop, m, new Paint()); + crop = tmp; + } + } else if (mRotation > 0) { + Matrix m = new Matrix(); + m.setRotate(mRotation); + Bitmap tmp = Bitmap.createBitmap(crop, 0, 0, crop.getWidth(), + crop.getHeight(), m, true); + if (tmp != null) { + crop = tmp; + } + } + + if (mSaveCroppedBitmap) { + mCroppedBitmap = crop; + } + + // Get output compression format + CompressFormat cf = + convertExtensionToCompressFormat(getFileExtension(mOutputFormat)); + + // Compress to byte array + ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); + if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) { + // If we need to set to the wallpaper, set it + if (mSetWallpaper && mWPManager != null) { + if (mWPManager == null) { + Log.w(LOGTAG, "no wallpaper manager"); + failure = true; + } else { + try { + mWPManager.setStream(new ByteArrayInputStream(tmpOut + .toByteArray())); + updateWallpaperDimensions(mOutWidth, mOutHeight); + } catch (IOException e) { + Log.w(LOGTAG, "cannot write stream to wallpaper", e); + failure = true; + } + } + } + } else { + Log.w(LOGTAG, "cannot compress bitmap"); + failure = true; + } + } + return !failure; // True if any of the operations failed + } + + @Override + protected Boolean doInBackground(Void... params) { + return cropBitmap(); + } + + @Override + protected void onPostExecute(Boolean result) { + setResult(Activity.RESULT_OK); + finish(); + } + } + + protected void updateWallpaperDimensions(int width, int height) { + String spKey = LauncherAppState.getSharedPreferencesKey(); + SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + if (width != 0 && height != 0) { + editor.putInt(WALLPAPER_WIDTH_KEY, width); + editor.putInt(WALLPAPER_HEIGHT_KEY, height); + } else { + editor.remove(WALLPAPER_WIDTH_KEY); + editor.remove(WALLPAPER_HEIGHT_KEY); + } + editor.commit(); + WallpaperPickerActivity.suggestWallpaperDimension(getResources(), + sp, getWindowManager(), WallpaperManager.getInstance(this)); + } + + protected static CompressFormat convertExtensionToCompressFormat(String extension) { + return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG; + } + + protected static String getFileExtension(String requestFormat) { + String outputFormat = (requestFormat == null) + ? "jpg" + : requestFormat; + outputFormat = outputFormat.toLowerCase(); + return (outputFormat.equals("png") || outputFormat.equals("gif")) + ? "png" // We don't support gif compression. + : "jpg"; + } +} diff --git a/src/com/android/launcher3/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java new file mode 100644 index 000000000..03274213f --- /dev/null +++ b/src/com/android/launcher3/WallpaperPickerActivity.java @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2013 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.launcher3; + +import android.app.ActionBar; +import android.app.Activity; +import android.app.WallpaperManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LevelListDrawable; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.SpinnerAdapter; +import android.widget.TextView; + +import com.android.photos.BitmapRegionTileSource; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class WallpaperPickerActivity extends WallpaperCropActivity { + private static final String TAG = "Launcher.WallpaperPickerActivity"; + + private static final int IMAGE_PICK = 5; + private static final float WALLPAPER_SCREENS_SPAN = 2f; + + private ArrayList<Integer> mThumbs; + private ArrayList<Integer> mImages; + + private View mSelectedThumb; + private CropView mCropView; + + private static class ThumbnailMetaData { + public boolean mLaunchesGallery; + public Uri mGalleryImageUri; + public int mWallpaperResId; + } + + private OnClickListener mThumbnailOnClickListener = new OnClickListener() { + public void onClick(View v) { + if (mSelectedThumb != null) { + mSelectedThumb.setSelected(false); + } + + ThumbnailMetaData meta = (ThumbnailMetaData) v.getTag(); + + if (!meta.mLaunchesGallery) { + mSelectedThumb = v; + v.setSelected(true); + } + + if (meta.mLaunchesGallery) { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/*"); + startActivityForResult(intent, IMAGE_PICK); + } else if (meta.mGalleryImageUri != null) { + mCropView.setTileSource(new BitmapRegionTileSource(WallpaperPickerActivity.this, + meta.mGalleryImageUri, 1024, 0), null); + } else { + mCropView.setTileSource(new BitmapRegionTileSource(WallpaperPickerActivity.this, + meta.mWallpaperResId, 1024, 0), null); + } + } + }; + + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == IMAGE_PICK && resultCode == RESULT_OK) { + Uri uri = data.getData(); + + // Add a tile for the image picked from Gallery + LinearLayout wallpapers = (LinearLayout) findViewById(R.id.wallpaper_list); + FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater(). + inflate(R.layout.wallpaper_picker_item, wallpapers, false); + setWallpaperItemPaddingToZero(pickedImageThumbnail); + + // Load the thumbnail + ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image); + + Resources res = getResources(); + int width = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth); + int height = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight); + + BitmapCropTask cropTask = new BitmapCropTask(uri, null, width, height, false, true); + Point bounds = cropTask.getImageBounds(); + + RectF cropRect = new RectF(); + // Get a crop rect that will fit this + if (bounds.x / (float) bounds.y > width / (float) height) { + cropRect.top = 0; + cropRect.bottom = bounds.y; + cropRect.left = (bounds.x - (width / (float) height) * bounds.y) / 2; + cropRect.right = bounds.x - cropRect.left; + } else { + cropRect.left = 0; + cropRect.right = bounds.x; + cropRect.top = (bounds.y - (height / (float) width) * bounds.x) / 2; + cropRect.bottom = bounds.y - cropRect.top; + } + cropTask.setCropBounds(cropRect); + + if (cropTask.cropBitmap()) { + image.setImageBitmap(cropTask.getCroppedBitmap()); + Drawable thumbDrawable = image.getDrawable(); + thumbDrawable.setDither(true); + } else { + Log.e(TAG, "Error loading thumbnail for uri=" + uri); + } + wallpapers.addView(pickedImageThumbnail, 0); + + ThumbnailMetaData meta = new ThumbnailMetaData(); + meta.mGalleryImageUri = uri; + pickedImageThumbnail.setTag(meta); + mThumbnailOnClickListener.onClick(pickedImageThumbnail); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.wallpaper_picker); + + mCropView = (CropView) findViewById(R.id.cropView); + + // Populate the built-in wallpapers + findWallpapers(); + + LinearLayout wallpapers = (LinearLayout) findViewById(R.id.wallpaper_list); + ImageAdapter ia = new ImageAdapter(this); + for (int i = 0; i < ia.getCount(); i++) { + FrameLayout thumbnail = (FrameLayout) ia.getView(i, null, wallpapers); + wallpapers.addView(thumbnail, i); + + ThumbnailMetaData meta = new ThumbnailMetaData(); + meta.mWallpaperResId = mImages.get(i); + thumbnail.setTag(meta); + thumbnail.setOnClickListener(mThumbnailOnClickListener); + if (i == 0) { + mThumbnailOnClickListener.onClick(thumbnail); + } + } + // Add a tile for the Gallery + FrameLayout galleryThumbnail = (FrameLayout) getLayoutInflater(). + inflate(R.layout.wallpaper_picker_gallery_item, wallpapers, false); + setWallpaperItemPaddingToZero(galleryThumbnail); + + TextView galleryLabel = + (TextView) galleryThumbnail.findViewById(R.id.wallpaper_item_label); + galleryLabel.setText(R.string.gallery); + wallpapers.addView(galleryThumbnail, 0); + + ThumbnailMetaData meta = new ThumbnailMetaData(); + meta.mLaunchesGallery = true; + galleryThumbnail.setTag(meta); + galleryThumbnail.setOnClickListener(mThumbnailOnClickListener); + + // Action bar + // Show the custom action bar view + final ActionBar actionBar = getActionBar(); + actionBar.setCustomView(R.layout.actionbar_set_wallpaper); + actionBar.getCustomView().setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + + ThumbnailMetaData meta = (ThumbnailMetaData) mSelectedThumb.getTag(); + if (meta.mLaunchesGallery) { + // shouldn't be selected, but do nothing + } else if (meta.mGalleryImageUri != null) { + // Get the crop + // TODO: get outwidth/outheight more robustly? + BitmapCropTask cropTask = new BitmapCropTask(meta.mGalleryImageUri, + mCropView.getCrop(), mCropView.getWidth(), mCropView.getHeight(), + true, false); + + cropTask.execute(); + } else if (meta.mWallpaperResId != 0) { + try { + WallpaperManager wm = + WallpaperManager.getInstance(getApplicationContext()); + wm.setResource(meta.mWallpaperResId); + // passing 0 will just revert back to using the default wallpaper + // size (setWallpaperDimension) + updateWallpaperDimensions(0, 0); + String spKey = LauncherAppState.getSharedPreferencesKey(); + SharedPreferences sp = + getSharedPreferences(spKey, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + editor.remove(WALLPAPER_WIDTH_KEY); + editor.remove(WALLPAPER_HEIGHT_KEY); + editor.commit(); + setResult(Activity.RESULT_OK); + finish(); + } catch (IOException e) { + Log.e(TAG, "Failed to set wallpaper: " + e); + } + } + } + }); + } + + private static void setWallpaperItemPaddingToZero(FrameLayout frameLayout) { + frameLayout.setPadding(0, 0, 0, 0); + frameLayout.setForeground(new ZeroPaddingDrawable(frameLayout.getForeground())); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + final Intent pickWallpaperIntent = new Intent(Intent.ACTION_SET_WALLPAPER); + final PackageManager pm = getPackageManager(); + final List<ResolveInfo> apps = + pm.queryIntentActivities(pickWallpaperIntent, 0); + + SubMenu sub = menu.addSubMenu("Other\u2026"); // TODO: what's the better way to do this? + sub.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + + // Get list of image picker intents + Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT); + pickImageIntent.setType("image/*"); + final List<ResolveInfo> imagePickerActivities = + pm.queryIntentActivities(pickImageIntent, 0); + final ComponentName[] imageActivities = new ComponentName[imagePickerActivities.size()]; + for (int i = 0; i < imagePickerActivities.size(); i++) { + ActivityInfo activityInfo = imagePickerActivities.get(i).activityInfo; + imageActivities[i] = new ComponentName(activityInfo.packageName, activityInfo.name); + } + + outerLoop: + for (ResolveInfo info : apps) { + final ComponentName componentName = + new ComponentName(info.activityInfo.packageName, info.activityInfo.name); + // Exclude anything from our own package, and the old Launcher + if (componentName.getPackageName().equals(getPackageName()) || + componentName.getPackageName().equals("com.android.launcher")) { + continue; + } + // Exclude any package that already responds to the image picker intent + for (ResolveInfo imagePickerActivityInfo : imagePickerActivities) { + if (componentName.getPackageName().equals( + imagePickerActivityInfo.activityInfo.packageName)) { + continue outerLoop; + } + } + MenuItem mi = sub.add(info.loadLabel(pm)); + Drawable icon = info.loadIcon(pm); + if (icon != null) { + mi.setIcon(icon); + } + } + return super.onCreateOptionsMenu(menu); + } + + private void findWallpapers() { + mThumbs = new ArrayList<Integer>(24); + mImages = new ArrayList<Integer>(24); + + final Resources resources = getResources(); + // Context.getPackageName() may return the "original" package name, + // com.android.launcher3; Resources needs the real package name, + // com.android.launcher3. So we ask Resources for what it thinks the + // package name should be. + final String packageName = resources.getResourcePackageName(R.array.wallpapers); + + addWallpapers(resources, packageName, R.array.wallpapers); + addWallpapers(resources, packageName, R.array.extra_wallpapers); + } + + private void addWallpapers(Resources resources, String packageName, int list) { + final String[] extras = resources.getStringArray(list); + for (String extra : extras) { + int res = resources.getIdentifier(extra, "drawable", packageName); + if (res != 0) { + final int thumbRes = resources.getIdentifier(extra + "_small", + "drawable", packageName); + + if (thumbRes != 0) { + mThumbs.add(thumbRes); + mImages.add(res); + // Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")"); + } + } + } + } + + // As a ratio of screen height, the total distance we want the parallax effect to span + // horizontally + private static float wallpaperTravelToScreenWidthRatio(int width, int height) { + float aspectRatio = width / (float) height; + + // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width + // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width + // We will use these two data points to extrapolate how much the wallpaper parallax effect + // to span (ie travel) at any aspect ratio: + + final float ASPECT_RATIO_LANDSCAPE = 16/10f; + final float ASPECT_RATIO_PORTRAIT = 10/16f; + final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; + final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; + + // To find out the desired width at different aspect ratios, we use the following two + // formulas, where the coefficient on x is the aspect ratio (width/height): + // (16/10)x + y = 1.5 + // (10/16)x + y = 1.2 + // We solve for x and y and end up with a final formula: + final float x = + (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / + (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); + final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; + return x * aspectRatio + y; + } + + static public void suggestWallpaperDimension(Resources res, + final SharedPreferences sharedPrefs, + WindowManager windowManager, + final WallpaperManager wallpaperManager) { + Point minDims = new Point(); + Point maxDims = new Point(); + windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); + + final int maxDim = Math.max(maxDims.x, maxDims.y); + final int minDim = Math.min(minDims.x, minDims.y); + + // We need to ensure that there is enough extra space in the wallpaper + // for the intended + // parallax effects + final int defaultWidth, defaultHeight; + if (LauncherAppState.isScreenLarge(res)) { + defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); + defaultHeight = maxDim; + } else { + defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim); + defaultHeight = maxDim; + } + new Thread("suggestWallpaperDimension") { + public void run() { + // If we have saved a wallpaper width/height, use that instead + int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWidth); + int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultHeight); + } + }.start(); + } + + static class ZeroPaddingDrawable extends LevelListDrawable { + public ZeroPaddingDrawable(Drawable d) { + super(); + addLevel(0, 0, d); + setLevel(0); + } + + @Override + public boolean getPadding(Rect padding) { + padding.set(0, 0, 0, 0); + return true; + } + } + + private class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter { + private LayoutInflater mLayoutInflater; + + ImageAdapter(Activity activity) { + mLayoutInflater = activity.getLayoutInflater(); + } + + public int getCount() { + return mThumbs.size(); + } + + public Object getItem(int position) { + return position; + } + + public long getItemId(int position) { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View view; + + if (convertView == null) { + view = mLayoutInflater.inflate(R.layout.wallpaper_picker_item, parent, false); + } else { + view = convertView; + } + + setWallpaperItemPaddingToZero((FrameLayout) view); + + ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image); + + int thumbRes = mThumbs.get(position); + image.setImageResource(thumbRes); + Drawable thumbDrawable = image.getDrawable(); + if (thumbDrawable != null) { + thumbDrawable.setDither(true); + } else { + Log.e(TAG, "Error decoding thumbnail resId=" + thumbRes + " for wallpaper #" + + position); + } + + return view; + } + } +} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b2f74330f..2298c5392 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -104,7 +104,6 @@ public class Workspace extends SmoothPagedView private LayoutTransition mLayoutTransition; private final WallpaperManager mWallpaperManager; private IBinder mWindowToken; - private static final float WALLPAPER_SCREENS_SPAN = 2f; private int mDefaultPage; @@ -189,16 +188,11 @@ public class Workspace extends SmoothPagedView public static final int DRAG_BITMAP_PADDING = 2; private boolean mWorkspaceFadeInAdjacentScreens; - enum WallpaperVerticalOffset { TOP, MIDDLE, BOTTOM }; - int mWallpaperWidth; - int mWallpaperHeight; WallpaperOffsetInterpolator mWallpaperOffset; boolean mUpdateWallpaperOffsetImmediately = false; private Runnable mDelayedResizeRunnable; private Runnable mDelayedSnapToPageRunnable; private Point mDisplaySize = new Point(); - private boolean mIsStaticWallpaper; - private int mWallpaperTravelWidth; private int mCameraDistance; // Variables relating to the creation of user folders by hovering shortcuts over shortcuts @@ -397,8 +391,6 @@ public class Workspace extends SmoothPagedView mWallpaperOffset = new WallpaperOffsetInterpolator(); Display display = mLauncher.getWindowManager().getDefaultDisplay(); display.getSize(mDisplaySize); - mWallpaperTravelWidth = (int) (mDisplaySize.x * - wallpaperTravelToScreenWidthRatio(mDisplaySize.x, mDisplaySize.y)); mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx); mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity); @@ -529,7 +521,7 @@ public class Workspace extends SmoothPagedView mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID); - long newId = LauncherAppState.getInstance().getLauncherProvider().generateNewScreenId(); + long newId = LauncherAppState.getLauncherProvider().generateNewScreenId(); mWorkspaceScreens.put(newId, cl); mScreenOrder.add(newId); @@ -874,7 +866,6 @@ public class Workspace extends SmoothPagedView // Only show page outlines as we pan if we are on large screen if (LauncherAppState.getInstance().isScreenLarge()) { showOutlines(); - mIsStaticWallpaper = mWallpaperManager.getWallpaperInfo() == null; } // If we are not fading in adjacent screens, we still need to restore the alpha in case the @@ -944,55 +935,9 @@ public class Workspace extends SmoothPagedView Launcher.setScreen(mCurrentPage); }; - // As a ratio of screen height, the total distance we want the parallax effect to span - // horizontally - private float wallpaperTravelToScreenWidthRatio(int width, int height) { - float aspectRatio = width / (float) height; - - // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width - // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width - // We will use these two data points to extrapolate how much the wallpaper parallax effect - // to span (ie travel) at any aspect ratio: - - final float ASPECT_RATIO_LANDSCAPE = 16/10f; - final float ASPECT_RATIO_PORTRAIT = 10/16f; - final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; - final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; - - // To find out the desired width at different aspect ratios, we use the following two - // formulas, where the coefficient on x is the aspect ratio (width/height): - // (16/10)x + y = 1.5 - // (10/16)x + y = 1.2 - // We solve for x and y and end up with a final formula: - final float x = - (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / - (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); - final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; - return x * aspectRatio + y; - } - protected void setWallpaperDimension() { - Point minDims = new Point(); - Point maxDims = new Point(); - mLauncher.getWindowManager().getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); - - final int maxDim = Math.max(maxDims.x, maxDims.y); - final int minDim = Math.min(minDims.x, minDims.y); - - // We need to ensure that there is enough extra space in the wallpaper for the intended - // parallax effects - if (LauncherAppState.getInstance().isScreenLarge()) { - mWallpaperWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); - mWallpaperHeight = maxDim; - } else { - mWallpaperWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim); - mWallpaperHeight = maxDim; - } - new Thread("setWallpaperDimension") { - public void run() { - mWallpaperManager.suggestDesiredDimensions(mWallpaperWidth, mWallpaperHeight); - } - }.start(); + WallpaperPickerActivity.suggestWallpaperDimension(mLauncher.getResources(), + mLauncher.getSharedPrefs(), mLauncher.getWindowManager(), mWallpaperManager); } private void syncWallpaperOffsetWithScroll() { |