summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClark Scheff <clark@cyngn.com>2014-09-10 11:43:54 -0700
committerClark Scheff <clark@cyngn.com>2014-09-15 17:49:16 -0700
commit5006280925944a3fc31c41900c242fb21dedfcdd (patch)
tree9a967f34564d6946654bd2a3ad4d1b825e5db597
parentd3fd7f67c589f24b51f92cee09acbcda8c1e6fb3 (diff)
downloadpackages_apps_ThemeChooser-5006280925944a3fc31c41900c242fb21dedfcdd.zip
packages_apps_ThemeChooser-5006280925944a3fc31c41900c242fb21dedfcdd.tar.gz
packages_apps_ThemeChooser-5006280925944a3fc31c41900c242fb21dedfcdd.tar.bz2
Add ability to pick external images for wallpaper/lockscreen
Change-Id: I22d1a00f794946a3b9671b03465d688df70f5c33
-rw-r--r--AndroidManifest.xml5
-rw-r--r--res/drawable-hdpi/img_wallpaper_external.pngbin0 -> 3127 bytes
-rw-r--r--res/drawable-mdpi/img_wallpaper_external.pngbin0 -> 2946 bytes
-rw-r--r--res/drawable-xhdpi/img_wallpaper_external.pngbin0 -> 3315 bytes
-rw-r--r--res/drawable-xxhdpi/img_wallpaper_external.pngbin0 -> 3782 bytes
-rw-r--r--res/values/strings.xml2
-rw-r--r--src/com/cyngn/theme/chooser/ChooserActivity.java58
-rw-r--r--src/com/cyngn/theme/chooser/ComponentSelector.java12
-rw-r--r--src/com/cyngn/theme/chooser/MyThemeFragment.java35
-rw-r--r--src/com/cyngn/theme/chooser/ThemeFragment.java145
-rw-r--r--src/com/cyngn/theme/chooser/WallpaperCardView.java4
-rw-r--r--src/com/cyngn/theme/util/Utils.java2
-rw-r--r--src/com/cyngn/theme/util/WallpaperUtils.java426
13 files changed, 667 insertions, 22 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bc5b024..11cfc0d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -19,6 +19,11 @@
<!-- Used to see if we are resuming from the launcher -->
<uses-permission android:name="android.permission.GET_TASKS" />
+ <!-- Used to support wallpapers from external sources -->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.SET_WALLPAPER" />
+ <uses-permission android:name="android.permission.SET_KEYGUARD_WALLPAPER" />
+
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="19" />
diff --git a/res/drawable-hdpi/img_wallpaper_external.png b/res/drawable-hdpi/img_wallpaper_external.png
new file mode 100644
index 0000000..669ed02
--- /dev/null
+++ b/res/drawable-hdpi/img_wallpaper_external.png
Binary files differ
diff --git a/res/drawable-mdpi/img_wallpaper_external.png b/res/drawable-mdpi/img_wallpaper_external.png
new file mode 100644
index 0000000..397107d
--- /dev/null
+++ b/res/drawable-mdpi/img_wallpaper_external.png
Binary files differ
diff --git a/res/drawable-xhdpi/img_wallpaper_external.png b/res/drawable-xhdpi/img_wallpaper_external.png
new file mode 100644
index 0000000..85bea23
--- /dev/null
+++ b/res/drawable-xhdpi/img_wallpaper_external.png
Binary files differ
diff --git a/res/drawable-xxhdpi/img_wallpaper_external.png b/res/drawable-xxhdpi/img_wallpaper_external.png
new file mode 100644
index 0000000..c10ee1b
--- /dev/null
+++ b/res/drawable-xxhdpi/img_wallpaper_external.png
Binary files differ
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 55e72a5..83adc62 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -66,4 +66,6 @@
<string name="reset_theme">Reset</string>
<string name="customize_theme">Customize</string>
+ <string name="wallpaper_external_title">Pick image</string>
+
</resources>
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);
+ }
+ }
+}