summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3
diff options
context:
space:
mode:
authorMichael Jurka <mikejurka@google.com>2013-07-08 18:03:46 -0700
committerMichael Jurka <mikejurka@google.com>2013-08-21 13:18:29 +0200
commit104c45677660586026a7e74ef8c47d396403d50e (patch)
treef9a2a66390820d69963ec626990d4bc0e58a7c02 /src/com/android/launcher3
parent912d360cd7350d8fe959202c6b5d0a5aa97790aa (diff)
downloadandroid_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.java168
-rw-r--r--src/com/android/launcher3/Launcher.java20
-rw-r--r--src/com/android/launcher3/LauncherAppState.java8
-rw-r--r--src/com/android/launcher3/PreloadReceiver.java3
-rw-r--r--src/com/android/launcher3/WallpaperCropActivity.java289
-rw-r--r--src/com/android/launcher3/WallpaperPickerActivity.java441
-rw-r--r--src/com/android/launcher3/Workspace.java61
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() {