diff options
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/cyngn/theme/chooser/ChooserActivity.java | 58 | ||||
-rw-r--r-- | src/com/cyngn/theme/chooser/ComponentSelector.java | 12 | ||||
-rw-r--r-- | src/com/cyngn/theme/chooser/MyThemeFragment.java | 35 | ||||
-rw-r--r-- | src/com/cyngn/theme/chooser/ThemeFragment.java | 145 | ||||
-rw-r--r-- | src/com/cyngn/theme/chooser/WallpaperCardView.java | 4 | ||||
-rw-r--r-- | src/com/cyngn/theme/util/Utils.java | 2 | ||||
-rw-r--r-- | src/com/cyngn/theme/util/WallpaperUtils.java | 426 |
7 files changed, 660 insertions, 22 deletions
diff --git a/src/com/cyngn/theme/chooser/ChooserActivity.java b/src/com/cyngn/theme/chooser/ChooserActivity.java index 0cf1c8d..0b7425c 100644 --- a/src/com/cyngn/theme/chooser/ChooserActivity.java +++ b/src/com/cyngn/theme/chooser/ChooserActivity.java @@ -78,6 +78,17 @@ public class ChooserActivity extends FragmentActivity private static final String THEME_STORE_PACKAGE = "com.cyngn.theme.store"; private static final String THEME_STORE_ACTIVITY = "com.cyngn.theme.store.StoreActivity"; + private static final String TYPE_IMAGE = "image/*"; + + /** + * Request code for picking an external wallpaper + */ + public static final int REQUEST_PICK_WALLPAPER_IMAGE = 2; + /** + * Request code for picking an external lockscreen wallpaper + */ + public static final int REQUEST_PICK_LOCKSCREEN_IMAGE = 3; + private static final long ANIMATE_CONTENT_IN_SCALE_DURATION = 500; private static final long ANIMATE_CONTENT_IN_ALPHA_DURATION = 750; private static final long ANIMATE_CONTENT_IN_BLUR_DURATION = 250; @@ -113,6 +124,8 @@ public class ChooserActivity extends FragmentActivity // Current system theme configuration as component -> pkgName private Map<String, String> mCurrentTheme = new HashMap<String, String>(); + protected boolean mIsPickingImage = false; + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); @@ -355,7 +368,7 @@ public class ChooserActivity extends FragmentActivity public void themeChangeEnd(boolean isSuccess) { mThemeChanging = false; ThemeFragment f = getCurrentFragment(); - if (f != null) { + if (f != null) { // We currently need to recreate the adapter in order to load // the changes otherwise the adapter returns the original fragments // TODO: We'll need a better way to handle this to provide a good UX @@ -430,6 +443,20 @@ public class ChooserActivity extends FragmentActivity } } + public void pickExternalWallpaper() { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType(TYPE_IMAGE); + startActivityForResult(intent, REQUEST_PICK_WALLPAPER_IMAGE); + mIsPickingImage = true; + } + + public void pickExternalLockscreen() { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType(TYPE_IMAGE); + startActivityForResult(intent, REQUEST_PICK_LOCKSCREEN_IMAGE); + mIsPickingImage = true; + } + private void slideContentIntoView(int yDelta, int selectorHeight) { ThemeFragment f = getCurrentFragment(); if (f != null) { @@ -465,7 +492,11 @@ public class ChooserActivity extends FragmentActivity mPager.setAdapter(mAdapter); mThemeChanging = false; } - getSupportLoaderManager().restartLoader(LOADER_ID_APPLIED, null, this); + if (!mIsPickingImage) { + getSupportLoaderManager().restartLoader(LOADER_ID_APPLIED, null, this); + } else { + mIsPickingImage = false; + } IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); registerReceiver(mWallpaperChangeReceiver, filter); @@ -549,6 +580,29 @@ public class ChooserActivity extends FragmentActivity mAnimateContentInDelay = ANIMATE_CONTENT_DELAY; } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK && requestCode == REQUEST_PICK_WALLPAPER_IMAGE) { + if (data != null && data.getData() != null) { + Uri uri = data.getData(); + ThemeFragment f = getCurrentFragment(); + if (f != null) { + f.setWallpaperImageUri(uri); + } + } + } else if (resultCode == RESULT_OK && requestCode == REQUEST_PICK_LOCKSCREEN_IMAGE) { + if (data != null && data.getData() != null) { + Uri uri = data.getData(); + ThemeFragment f = getCurrentFragment(); + if (f != null) { + f.setLockscreenImageUri(uri); + } + } + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + private void animateContentIn() { Drawable d = mCustomBackground.getDrawable(); if (d instanceof TransitionDrawable) { diff --git a/src/com/cyngn/theme/chooser/ComponentSelector.java b/src/com/cyngn/theme/chooser/ComponentSelector.java index 4bf0ec4..73dbd0f 100644 --- a/src/com/cyngn/theme/chooser/ComponentSelector.java +++ b/src/com/cyngn/theme/chooser/ComponentSelector.java @@ -62,6 +62,8 @@ public class ComponentSelector extends LinearLayout public static final boolean DEBUG_SELECTOR = false; + public static final String EXTERNAL_WALLPAPER = "external"; + private static final int LOADER_ID_STATUS_BAR = 100; private static final int LOADER_ID_NAVIGATION_BAR = 101; private static final int LOADER_ID_FONT = 102; @@ -461,7 +463,8 @@ public class ComponentSelector extends LinearLayout int count = mCursor.getCount(); if (mCurrentLoaderId == LOADER_ID_WALLPAPER || mCurrentLoaderId == LOADER_ID_LOCKSCREEN) { - count++; + // Wallpaper and lockscreen have 2 additional options (none, and pick image). + count += 2; } return (int) Math.ceil((float)count / mItemsPerPage); } @@ -627,8 +630,13 @@ public class ComponentSelector extends LinearLayout if (index == 0) { iv.setImageResource(R.drawable.img_wallpaper_none); v.setTag(""); + } else if (index == 1) { + iv.setImageResource(R.drawable.img_wallpaper_external); + v.setTag(EXTERNAL_WALLPAPER); + ((TextView) v.findViewById(R.id.title)).setText( + mContext.getString(R.string.wallpaper_external_title)); } else { - cursor.moveToPosition(index - 1); + cursor.moveToPosition(index - 2); int pkgNameIndex = cursor.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME); iv.setImageBitmap( Utils.loadBitmapBlob(cursor, wallpaperIndex)); diff --git a/src/com/cyngn/theme/chooser/MyThemeFragment.java b/src/com/cyngn/theme/chooser/MyThemeFragment.java index d5f0b4a..c252785 100644 --- a/src/com/cyngn/theme/chooser/MyThemeFragment.java +++ b/src/com/cyngn/theme/chooser/MyThemeFragment.java @@ -4,7 +4,10 @@ package com.cyngn.theme.chooser; import android.app.WallpaperManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ThemeUtils; import android.content.res.Resources; import android.database.Cursor; @@ -92,9 +95,19 @@ public class MyThemeFragment extends ThemeFragment { @Override public void onResume() { super.onResume(); - if (getLoaderManager().getLoader(0) != null) { + if (mExternalWallpaperUri == null && mExternalLockscreenUri == null + && getLoaderManager().getLoader(0) != null) { getLoaderManager().restartLoader(0, null, this); } + + IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); + getActivity().registerReceiver(mWallpaperChangeReceiver, filter); + } + + @Override + public void onPause() { + getActivity().unregisterReceiver(mWallpaperChangeReceiver); + super.onPause(); } @Override @@ -150,6 +163,24 @@ public class MyThemeFragment extends ThemeFragment { } } + private BroadcastReceiver mWallpaperChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final WallpaperManager wm = WallpaperManager.getInstance(context); + if (wm.getWallpaperInfo() != null) { + addSurfaceView(mSurfaceView); + } else { + removeSurfaceView(mSurfaceView); + } + + Drawable wp = context == null ? null : wm.getDrawable(); + if (wp != null) { + mWallpaper.setImageDrawable(wp); + mWallpaperCard.setWallpaper(wp); + } + } + }; + private void setCustomizedTagIfCustomized() { boolean isDefault = mBaseThemePkgName.equals(ThemeUtils.getDefaultThemePackageName(getActivity())); @@ -246,6 +277,7 @@ public class MyThemeFragment extends ThemeFragment { @Override protected void loadWallpaper(Cursor c, boolean animate) { + mExternalWallpaperUri = null; int pkgNameIdx = c.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME); if (pkgNameIdx > -1) { super.loadWallpaper(c, animate); @@ -288,6 +320,7 @@ public class MyThemeFragment extends ThemeFragment { @Override protected void loadLockScreen(Cursor c, boolean animate) { + mExternalLockscreenUri = null; int pkgNameIdx = c.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME); if (pkgNameIdx > -1) { super.loadLockScreen(c, animate); diff --git a/src/com/cyngn/theme/chooser/ThemeFragment.java b/src/com/cyngn/theme/chooser/ThemeFragment.java index 541895e..2a4c60a 100644 --- a/src/com/cyngn/theme/chooser/ThemeFragment.java +++ b/src/com/cyngn/theme/chooser/ThemeFragment.java @@ -8,6 +8,9 @@ import android.animation.AnimatorSet; import android.animation.IntEvaluator; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.app.Activity; +import android.app.WallpaperManager; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -65,7 +68,6 @@ import android.widget.PopupMenu; import android.widget.ProgressBar; import android.widget.Space; import android.widget.TextView; -import android.widget.Toast; import com.cyngn.theme.chooser.ComponentSelector.OnItemClickedListener; import com.cyngn.theme.util.AudioUtils; @@ -75,6 +77,7 @@ import com.cyngn.theme.util.PreferenceUtils; import com.cyngn.theme.util.ThemedTypefaceHelper; import com.cyngn.theme.util.TypefaceHelperCache; import com.cyngn.theme.util.Utils; +import com.cyngn.theme.util.WallpaperUtils; import com.cyngn.theme.widget.BootAniImageView; import com.cyngn.theme.widget.LockableScrollView; import com.cyngn.theme.widget.ThemeTagLayout; @@ -115,7 +118,6 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb public static final int ANIMATE_PROGRESS_OUT_DURATION = 400; public static final int ANIMATE_TITLE_IN_DURATION = 500; public static final int ANIMATE_APPLY_LAYOUT_DURATION = 300; - public static final int REQUEST_UNINSTALL = 1; // Request code public static final String CURRENTLY_APPLIED_THEME = "currently_applied_theme"; @@ -256,6 +258,9 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb protected View mClickableView; protected String mBaseThemePkgName; + protected Uri mExternalWallpaperUri; + protected Uri mExternalLockscreenUri; + protected enum CustomizeResetAction { Customize, Reset, @@ -499,6 +504,36 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb } } + public void setWallpaperImageUri(Uri uri) { + mExternalWallpaperUri = uri; + final Point size = new Point(mWallpaper.getWidth(), mWallpaper.getHeight()); + final Drawable wp = getWallpaperDrawableFromUri(uri, size); + mWallpaperCard.setWallpaper(wp); + mWallpaper.setImageDrawable(wp); + // remove the entry from mSelectedComponentsMap + mSelectedComponentsMap.remove(ThemesColumns.MODIFIES_LAUNCHER); + } + + public void setLockscreenImageUri(Uri uri) { + mExternalLockscreenUri = uri; + final Point size = new Point(mLockScreenCard.getWidth(), mLockScreenCard.getHeight()); + final Drawable wp = getWallpaperDrawableFromUri(uri, size); + mLockScreenCard.setWallpaper(wp); + // remove the entry from mSelectedComponentsMap + mSelectedComponentsMap.remove(ThemesColumns.MODIFIES_LOCKSCREEN); + } + + protected Drawable getWallpaperDrawableFromUri(Uri uri, Point size) { + final Context context = getActivity(); + final Resources res = context.getResources(); + Bitmap bmp = WallpaperUtils.createPreview(size, context, uri, null, res, + 0, 0, false); + if (bmp != null) { + return new BitmapDrawable(res, bmp); + } + return null; + } + protected ChooserActivity getChooserActivity() { return (ChooserActivity) getActivity(); } @@ -735,6 +770,16 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb if (pkgName != null && pkgName.length() == 0) { mWallpaper.setImageResource(R.drawable.wallpaper_none_bg); } + // we do this here instead of in applyTheme() because this can take a bit longer + // to propagate the change from WallpaperManager back to us + if (mExternalWallpaperUri != null) { + // Handle setting an external wallpaper in a separate thread + new Thread(mApplyExternalWallpaperRunnable).start(); + } + if (mExternalLockscreenUri != null) { + // Handle setting an external wallpaper in a separate thread + new Thread(mApplyExternalLockscreenRunnable).start(); + } } } @@ -1341,6 +1386,7 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb } protected void loadWallpaper(Cursor c, boolean animate) { + mExternalWallpaperUri = null; Drawable overlay = null; if (animate) { overlay = getOverlayDrawable(mWallpaperCard, true); @@ -1372,6 +1418,7 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb } protected void loadLockScreen(Cursor c, boolean animate) { + mExternalLockscreenUri = null; Drawable overlay = null; if (animate) { overlay = getOverlayDrawable(mLockScreenCard, true); @@ -1833,18 +1880,26 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb } else if (MODIFIES_NAVIGATION_BAR.equals(component)) { loaderId = LOADER_ID_NAVIGATION_BAR; } else if (MODIFIES_LAUNCHER.equals(component)) { - if (pkgName != null && TextUtils.isEmpty(pkgName)) { - mWallpaperCard.setWallpaper(null); - mSelectedComponentsMap.put(ThemesColumns.MODIFIES_LAUNCHER, ""); - setCardTitle(mWallpaperCard, "", getString(R.string.wallpaper_label)); - } else { - loaderId = LOADER_ID_WALLPAPER; + if (pkgName != null) { + if (TextUtils.isEmpty(pkgName)) { + mWallpaperCard.setWallpaper(null); + mSelectedComponentsMap.put(ThemesColumns.MODIFIES_LAUNCHER, ""); + setCardTitle(mWallpaperCard, "", getString(R.string.wallpaper_label)); + } else if (ComponentSelector.EXTERNAL_WALLPAPER.equals(pkgName)) { + getChooserActivity().pickExternalWallpaper(); + setCardTitle(mWallpaperCard, "", getString(R.string.wallpaper_label)); + } else { + loaderId = LOADER_ID_WALLPAPER; + } } } else if (MODIFIES_LOCKSCREEN.equals(component)) { if (pkgName != null && TextUtils.isEmpty(pkgName)) { mLockScreenCard.setWallpaper(null); mSelectedComponentsMap.put(ThemesColumns.MODIFIES_LOCKSCREEN, ""); - setCardTitle(mWallpaperCard, "", getString(R.string.lockscreen_label)); + setCardTitle(mLockScreenCard, "", getString(R.string.lockscreen_label)); + } else if (ComponentSelector.EXTERNAL_WALLPAPER.equals(pkgName)) { + getChooserActivity().pickExternalLockscreen(); + setCardTitle(mLockScreenCard, "", getString(R.string.lockscreen_label)); } else { loaderId = LOADER_ID_LOCKSCREEN; } @@ -1885,26 +1940,77 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb public void run() { final Context context = getActivity(); if (context != null) { - if (mSelectedComponentsMap != null && mSelectedComponentsMap.size() > 0) { - // Post this on mHandler so the client is added and removed from the same - // thread - mHandler.post(new Runnable() { - @Override - public void run() { + // Post this on mHandler so the client is added and removed from the same + // thread + mHandler.post(new Runnable() { + @Override + public void run() { + if (mSelectedComponentsMap != null && mSelectedComponentsMap.size() > 0) { ThemeManager tm = getThemeManager(); if (tm != null) { tm.addClient(ThemeFragment.this); tm.requestThemeChange(mSelectedComponentsMap); } + } else { + onFinish(true); } - }); + } + }); + } + } + }; + + private Runnable mApplyExternalWallpaperRunnable = new Runnable() { + @Override + public void run() { + // If an external image was selected for the wallpaper, we need to + // set that manually. + if (mExternalWallpaperUri != null) { + WallpaperManager wm = + WallpaperManager.getInstance(getActivity()); + final Context context = getActivity(); + final Resources res = context.getResources(); + final Point size = new Point(wm.getDesiredMinimumWidth(), + wm.getDesiredMinimumHeight()); + Bitmap bmp = WallpaperUtils.createPreview(size, context, mExternalWallpaperUri, + null, res, 0, 0, false); + try { + wm.setBitmap(bmp); + } catch (Exception e) { + Log.e(TAG, "Unable to set external wallpaper", e); + } + } + } + }; + + private Runnable mApplyExternalLockscreenRunnable = new Runnable() { + @Override + public void run() { + // If an external image was selected for the wallpaper, we need to + // set that manually. + if (mExternalLockscreenUri != null) { + WallpaperManager wm = + WallpaperManager.getInstance(getActivity()); + final Context context = getActivity(); + final Resources res = context.getResources(); + final Point size = new Point(); + ((Activity) context).getWindowManager().getDefaultDisplay().getRealSize(size); + Bitmap bmp = WallpaperUtils.createPreview(size, context, mExternalLockscreenUri, + null, res, 0, 0, false); + try { + wm.setKeyguardBitmap(bmp); + } catch (Exception e) { + Log.e(TAG, "Unable to set external lockscreen wallpaper", e); } } } }; protected void applyTheme() { - if (mSelectedComponentsMap == null || mSelectedComponentsMap.size() <= 0) return; + if (mExternalWallpaperUri == null && mExternalLockscreenUri == null && + (mSelectedComponentsMap == null || mSelectedComponentsMap.size() <= 0)) { + return; + } getChooserActivity().themeChangeStart(); animateProgressIn(mApplyThemeRunnable); } @@ -2122,6 +2228,9 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb } public boolean componentsChanged() { + // If an external wallpaper/ls are set then something changed! + if (mExternalWallpaperUri != null || mExternalLockscreenUri != null) return true; + for (String key : mSelectedComponentsMap.keySet()) { if (!mPkgName.equals(mSelectedComponentsMap.get(key))) { return true; @@ -2132,6 +2241,8 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb public void clearChanges() { mSelectedComponentsMap.clear(); + mExternalWallpaperUri = null; + mExternalLockscreenUri = null; getLoaderManager().restartLoader(LOADER_ID_ALL, null, ThemeFragment.this); } diff --git a/src/com/cyngn/theme/chooser/WallpaperCardView.java b/src/com/cyngn/theme/chooser/WallpaperCardView.java index f832302..3a6d554 100644 --- a/src/com/cyngn/theme/chooser/WallpaperCardView.java +++ b/src/com/cyngn/theme/chooser/WallpaperCardView.java @@ -74,6 +74,10 @@ public class WallpaperCardView extends ComponentCardView { } } + public Drawable getWallpaperDrawable() { + return mImage.getDrawable(); + } + @Override public void expand(boolean showLabel) { // Do nothing diff --git a/src/com/cyngn/theme/util/Utils.java b/src/com/cyngn/theme/util/Utils.java index 94fb5df..4afcb1b 100644 --- a/src/com/cyngn/theme/util/Utils.java +++ b/src/com/cyngn/theme/util/Utils.java @@ -18,6 +18,8 @@ import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Point; import android.graphics.Rect; +import android.net.Uri; +import android.provider.MediaStore; import android.provider.ThemesContract; import android.util.Log; import android.util.TypedValue; diff --git a/src/com/cyngn/theme/util/WallpaperUtils.java b/src/com/cyngn/theme/util/WallpaperUtils.java new file mode 100644 index 0000000..7f7536c --- /dev/null +++ b/src/com/cyngn/theme/util/WallpaperUtils.java @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2014 Cyanogen, Inc. + */ +package com.cyngn.theme.util; + +import android.app.WallpaperManager; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +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 java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +public class WallpaperUtils { + private static final String TAG = WallpaperUtils.class.getSimpleName(); + private static final int DEFAULT_COMPRESS_QUALITY = 90; + + /** + * createThumbnail from WallpaperCropActivity in f/b/packages/WallpaperCropper, renamed + * tp createPreview. + */ + public static Bitmap createPreview(Point size, Context context, Uri uri, byte[] imageBytes, + Resources res, int resId, int rotation, boolean leftAligned) { + int width = size.x; + int height = size.y; + + BitmapCropTask cropTask; + if (uri != null) { + cropTask = new BitmapCropTask( + context, uri, null, rotation, width, height, false, true, null); + } else if (imageBytes != null) { + cropTask = new BitmapCropTask( + imageBytes, null, rotation, width, height, false, true, null); + } else { + cropTask = new BitmapCropTask( + context, res, resId, null, rotation, width, height, false, true, null); + } + Point bounds = cropTask.getImageBounds(); + if (bounds == null || bounds.x == 0 || bounds.y == 0) { + return null; + } + + Matrix rotateMatrix = new Matrix(); + rotateMatrix.setRotate(rotation); + float[] rotatedBounds = new float[] { bounds.x, bounds.y }; + rotateMatrix.mapPoints(rotatedBounds); + rotatedBounds[0] = Math.abs(rotatedBounds[0]); + rotatedBounds[1] = Math.abs(rotatedBounds[1]); + + RectF cropRect = getMaxCropRect( + (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned); + cropTask.setCropBounds(cropRect); + + if (cropTask.cropBitmap()) { + return cropTask.getCroppedBitmap(); + } else { + return null; + } + } + + /** + * getMaxCropRect from WallpaperCropActivity in f/b/packages/WallpaperCropper + */ + protected static RectF getMaxCropRect( + int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) { + RectF cropRect = new RectF(); + // Get a crop rect that will fit this + if (inWidth / (float) inHeight > outWidth / (float) outHeight) { + cropRect.top = 0; + cropRect.bottom = inHeight; + cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2; + cropRect.right = inWidth - cropRect.left; + if (leftAligned) { + cropRect.right -= cropRect.left; + cropRect.left = 0; + } + } else { + cropRect.left = 0; + cropRect.right = inWidth; + cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2; + cropRect.bottom = inHeight - cropRect.top; + } + return cropRect; + } + + /** + * convertExtensionToCompressFormat from WallpaperCropActivity in f/b/packages/WallpaperCropper + */ + protected static Bitmap.CompressFormat convertExtensionToCompressFormat(String extension) { + return extension.equals("png") ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG; + } + + /** + * getFileExtension from WallpaperCropActivity in f/b/packages/WallpaperCropper + */ + 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"; + } + + /** + * BitmapCropTask from WallpaperCropActivity in f/b/packages/WallpaperCropper + */ + protected static class BitmapCropTask extends AsyncTask<Void, Void, Boolean> { + Uri mInUri = null; + Context mContext; + String mInFilePath; + byte[] mInImageBytes; + int mInResId = 0; + InputStream mInStream; + RectF mCropBounds = null; + int mOutWidth, mOutHeight; + int mRotation; + String mOutputFormat = "jpg"; // for now + boolean mSetWallpaper; + boolean mSaveCroppedBitmap; + Bitmap mCroppedBitmap; + Runnable mOnEndRunnable; + Resources mResources; + OnBitmapCroppedHandler mOnBitmapCroppedHandler; + boolean mNoCrop; + boolean mImageFromAsset; + + public BitmapCropTask(byte[] imageBytes, + RectF cropBounds, int rotation, int outWidth, int outHeight, + boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { + mInImageBytes = imageBytes; + init(cropBounds, rotation, + outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); + } + + public BitmapCropTask(Context c, Uri inUri, + RectF cropBounds, int rotation, int outWidth, int outHeight, + boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { + mContext = c; + mInUri = inUri; + init(cropBounds, rotation, + outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); + } + + public BitmapCropTask(Context c, Resources res, int inResId, + RectF cropBounds, int rotation, int outWidth, int outHeight, + boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { + mContext = c; + mInResId = inResId; + mResources = res; + init(cropBounds, rotation, + outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); + } + + private void init(RectF cropBounds, int rotation, int outWidth, int outHeight, + boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { + mCropBounds = cropBounds; + mRotation = rotation; + mOutWidth = outWidth; + mOutHeight = outHeight; + mSetWallpaper = setWallpaper; + mSaveCroppedBitmap = saveCroppedBitmap; + mOnEndRunnable = onEndRunnable; + } + + // Helper to setup input stream + private void regenerateInputStream() { + if (mInUri == null && mInResId == 0 && mInFilePath == null && + mInImageBytes == null && !mImageFromAsset) { + Log.w(TAG, "cannot read original file, no input URI, resource ID, or " + + "image byte array given"); + } else { + Utils.closeQuiet(mInStream); + try { + if (mInUri != null) { + mInStream = new BufferedInputStream( + mContext.getContentResolver().openInputStream(mInUri)); + } else if (mInFilePath != null) { + mInStream = mContext.openFileInput(mInFilePath); + } else if (mInImageBytes != null) { + mInStream = new BufferedInputStream( + new ByteArrayInputStream(mInImageBytes)); + } else { + mInStream = new BufferedInputStream( + mResources.openRawResource(mInResId)); + } + } catch (FileNotFoundException e) { + Log.w(TAG, "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(); + + WallpaperManager wallpaperManager = null; + if (mSetWallpaper) { + wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext()); + } + if (mSetWallpaper && mNoCrop && mInStream != null) { + try { + wallpaperManager.setStream(mInStream); + } catch (IOException e) { + Log.w(TAG, "cannot write stream to wallpaper", e); + failure = true; + } + return !failure; + } + if (mInStream != null) { + // Find crop bounds (scaled to original image size) + Rect roundedTrueCrop = new Rect(); + Matrix rotateMatrix = new Matrix(); + Matrix inverseRotateMatrix = new Matrix(); + if (mRotation > 0) { + rotateMatrix.setRotate(mRotation); + inverseRotateMatrix.setRotate(-mRotation); + + mCropBounds.roundOut(roundedTrueCrop); + mCropBounds = new RectF(roundedTrueCrop); + + Point bounds = getImageBounds(); + + float[] rotatedBounds = new float[] { bounds.x, bounds.y }; + rotateMatrix.mapPoints(rotatedBounds); + rotatedBounds[0] = Math.abs(rotatedBounds[0]); + rotatedBounds[1] = Math.abs(rotatedBounds[1]); + + mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2); + inverseRotateMatrix.mapRect(mCropBounds); + mCropBounds.offset(bounds.x/2, bounds.y/2); + + regenerateInputStream(); + } + + mCropBounds.roundOut(roundedTrueCrop); + + if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { + Log.w(TAG, "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(TAG, "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) { + mCropBounds.left /= scaleDownSampleSize; + mCropBounds.top /= scaleDownSampleSize; + mCropBounds.bottom /= scaleDownSampleSize; + mCropBounds.right /= scaleDownSampleSize; + mCropBounds.roundOut(roundedTrueCrop); + + crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, + roundedTrueCrop.top, roundedTrueCrop.width(), + roundedTrueCrop.height()); + } + } + + if (crop == null) { + Log.w(TAG, "cannot decode file: " + mInUri.toString()); + failure = true; + return false; + } + if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) { + float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() }; + rotateMatrix.mapPoints(dimsAfter); + dimsAfter[0] = Math.abs(dimsAfter[0]); + dimsAfter[1] = Math.abs(dimsAfter[1]); + + if (!(mOutWidth > 0 && mOutHeight > 0)) { + mOutWidth = Math.round(dimsAfter[0]); + mOutHeight = Math.round(dimsAfter[1]); + } + + RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]); + RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight); + + Matrix m = new Matrix(); + if (mRotation == 0) { + m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); + } else { + Matrix m1 = new Matrix(); + m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f); + Matrix m2 = new Matrix(); + m2.setRotate(mRotation); + Matrix m3 = new Matrix(); + m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f); + Matrix m4 = new Matrix(); + m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); + + Matrix c1 = new Matrix(); + c1.setConcat(m2, m1); + Matrix c2 = new Matrix(); + c2.setConcat(m4, m3); + m.setConcat(c2, c1); + } + + Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), + (int) returnRect.height(), Bitmap.Config.ARGB_8888); + if (tmp != null) { + Canvas c = new Canvas(tmp); + Paint p = new Paint(); + p.setFilterBitmap(true); + c.drawBitmap(crop, m, p); + crop = tmp; + } + } + + if (mSaveCroppedBitmap) { + mCroppedBitmap = crop; + } + + // Get output compression format + Bitmap.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 && wallpaperManager != null) { + try { + byte[] outByteArray = tmpOut.toByteArray(); + wallpaperManager.setStream(new ByteArrayInputStream(outByteArray)); + if (mOnBitmapCroppedHandler != null) { + mOnBitmapCroppedHandler.onBitmapCropped(outByteArray); + } + } catch (IOException e) { + Log.w(TAG, "cannot write stream to wallpaper", e); + failure = true; + } + } + } else { + Log.w(TAG, "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) { + if (mOnEndRunnable != null) { + mOnEndRunnable.run(); + } + } + + public interface OnBitmapCroppedHandler { + public void onBitmapCropped(byte[] imageBytes); + } + } +} |