summaryrefslogtreecommitdiffstats
path: root/src/org/cyanogenmod/theme/chooser2
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/cyanogenmod/theme/chooser2')
-rw-r--r--src/org/cyanogenmod/theme/chooser2/AppReceiver.java71
-rw-r--r--src/org/cyanogenmod/theme/chooser2/BootReceiver.java56
-rw-r--r--src/org/cyanogenmod/theme/chooser2/ChooserActivity.java1185
-rw-r--r--src/org/cyanogenmod/theme/chooser2/ComponentCardView.java232
-rw-r--r--src/org/cyanogenmod/theme/chooser2/ComponentSelector.java934
-rw-r--r--src/org/cyanogenmod/theme/chooser2/IconTransitionDrawable.java171
-rw-r--r--src/org/cyanogenmod/theme/chooser2/MyThemeFragment.java707
-rw-r--r--src/org/cyanogenmod/theme/chooser2/NewFragmentStatePagerAdapter.java333
-rw-r--r--src/org/cyanogenmod/theme/chooser2/NotificationHijackingService.java88
-rw-r--r--src/org/cyanogenmod/theme/chooser2/PagerContainer.java219
-rw-r--r--src/org/cyanogenmod/theme/chooser2/ThemeFragment.java3059
-rw-r--r--src/org/cyanogenmod/theme/chooser2/WallpaperCardView.java160
12 files changed, 7215 insertions, 0 deletions
diff --git a/src/org/cyanogenmod/theme/chooser2/AppReceiver.java b/src/org/cyanogenmod/theme/chooser2/AppReceiver.java
new file mode 100644
index 0000000..9ac3f91
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/AppReceiver.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import org.cyanogenmod.theme.util.NotificationHelper;
+import org.cyanogenmod.theme.util.PreferenceUtils;
+import org.cyanogenmod.theme.util.Utils;
+
+public class AppReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Uri uri = intent.getData();
+ String pkgName = uri != null ? uri.getSchemeSpecificPart() : null;
+ String action = intent.getAction();
+
+ if (cyanogenmod.content.Intent.ACTION_THEME_INSTALLED.equals(action)) {
+ if (!pkgName.equals(Utils.getDefaultThemePackageName(context))) {
+ NotificationHelper.postThemeInstalledNotification(context, pkgName);
+ }
+ } else if (cyanogenmod.content.Intent.ACTION_THEME_REMOVED.equals(action)) {
+ // remove updated status for this theme (if one exists)
+ PreferenceUtils.removeUpdatedTheme(context, pkgName);
+
+ // If the theme being removed was the currently applied theme we need
+ // to update the applied base theme in preferences to the default theme.
+ String appliedBaseTheme = PreferenceUtils.getAppliedBaseTheme(context);
+ if (!TextUtils.isEmpty(appliedBaseTheme) && appliedBaseTheme.equals(pkgName)) {
+ PreferenceUtils.setAppliedBaseTheme(context,
+ Utils.getDefaultThemePackageName(context));
+ }
+ NotificationHelper.cancelNotifications(context);
+ } else if (cyanogenmod.content.Intent.ACTION_THEME_UPDATED.equals(action)) {
+ try {
+ if (isTheme(context, pkgName)) {
+ PreferenceUtils.addUpdatedTheme(context, pkgName);
+ }
+ } catch (NameNotFoundException e) {
+ }
+ }
+ }
+
+ private boolean isTheme(Context context, String pkgName) throws NameNotFoundException {
+ PackageInfo pi = context.getPackageManager().getPackageInfo(pkgName, 0);
+
+ return pi != null && pi.themeInfo != null;
+ }
+}
diff --git a/src/org/cyanogenmod/theme/chooser2/BootReceiver.java b/src/org/cyanogenmod/theme/chooser2/BootReceiver.java
new file mode 100644
index 0000000..02dc107
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/BootReceiver.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+
+public class BootReceiver extends BroadcastReceiver {
+ private static final String CHOOSER_PKG_NAME = "org.cyanogenmod.theme.chooser";
+ private static final String CHOOSER_ACTIVITY = "org.cyanogenmod.theme.chooser.ChooserLauncher";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ PackageManager pm = context.getPackageManager();
+ if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+ try {
+ PackageInfo info = pm.getPackageInfo(ChooserActivity.THEME_STORE_PACKAGE, 0);
+ if (info != null) {
+ ComponentName cn = new ComponentName(CHOOSER_PKG_NAME, CHOOSER_ACTIVITY);
+ pm.setComponentEnabledSetting(cn,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // no store so nothing to do.
+ }
+
+ // now disable this receiver so we don't get called on future boots
+ ComponentName cn = new ComponentName(CHOOSER_PKG_NAME,
+ BootReceiver.class.getCanonicalName());
+ pm.setComponentEnabledSetting(cn,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ }
+ }
+}
diff --git a/src/org/cyanogenmod/theme/chooser2/ChooserActivity.java b/src/org/cyanogenmod/theme/chooser2/ChooserActivity.java
new file mode 100644
index 0000000..3e63dfa
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/ChooserActivity.java
@@ -0,0 +1,1185 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.ThemeConfig;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicBlur;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+import android.support.v4.view.ThemeViewPager;
+import android.support.v4.view.ViewPager;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.MutableLong;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import org.cyanogenmod.theme.perapptheming.PerAppThemingWindow;
+import org.cyanogenmod.theme.util.CursorLoaderHelper;
+import org.cyanogenmod.theme.util.NotificationHelper;
+import org.cyanogenmod.theme.util.PreferenceUtils;
+import org.cyanogenmod.theme.util.TypefaceHelperCache;
+import org.cyanogenmod.theme.util.Utils;
+
+import cyanogenmod.platform.Manifest;
+import cyanogenmod.providers.ThemesContract;
+import cyanogenmod.providers.ThemesContract.ThemesColumns;
+import cyanogenmod.themes.ThemeManager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_ALARMS;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_BOOT_ANIM;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_NOTIFICATIONS;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_RINGTONES;
+import static org.cyanogenmod.theme.chooser2.ComponentSelector.DEFAULT_COMPONENT_ID;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_INSTALLED_THEMES;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_APPLIED;
+
+public class ChooserActivity extends FragmentActivity
+ implements LoaderManager.LoaderCallbacks<Cursor> {
+ public static final String THEME_STORE_PACKAGE = "com.cyngn.theme.tore";
+ private static final String TAG = ChooserActivity.class.getSimpleName();
+
+ public static final String DEFAULT = ThemeConfig.SYSTEM_DEFAULT;
+ public static final String EXTRA_PKGNAME = "pkgName";
+ public static final String EXTRA_COMPONENTS = "components";
+
+ private static final int OFFSCREEN_PAGE_LIMIT = 3;
+
+ private static final String THEME_STORE_ACTIVITY = THEME_STORE_PACKAGE + ".ui.StoreActivity";
+ private static final String ACTION_APPLY_THEME = "android.intent.action.APPLY_THEME";
+
+ private static final String TYPE_IMAGE = "image/*";
+
+ private static final String ACTION_PICK_LOCK_SCREEN_WALLPAPER =
+ "com.cyngn.intent.action.PICK_LOCK_SCREEN_WALLPAPER";
+
+ /**
+ * 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;
+
+ /**
+ * Request code for enabling system alert window permission
+ */
+ private static final int REQUEST_SYSTEM_WINDOW_PERMISSION = 4;
+
+ 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;
+ private static final long ANIMATE_CONTENT_DELAY = 250;
+ private static final long ANIMATE_SHOP_THEMES_HIDE_DURATION = 250;
+ private static final long ANIMATE_SHOP_THEMES_SHOW_DURATION = 500;
+ private static final long FINISH_ANIMATION_DELAY = ThemeFragment.ANIMATE_DURATION
+ + ThemeFragment.ANIMATE_START_DELAY + 250;
+
+ private static final long ANIMATE_CARDS_IN_DURATION = 250;
+ private static final long ANIMATE_SAVE_APPLY_LAYOUT_DURATION = 300;
+ private static final float ANIMATE_SAVE_APPLY_DECELERATE_INTERPOLATOR_FACTOR = 3;
+ private static final long ONCLICK_SAVE_APPLY_FINISH_ANIMATION_DELAY = 400;
+
+ private PagerContainer mContainer;
+ private ThemeViewPager mPager;
+
+ private ThemesAdapter mAdapter;
+ private boolean mExpanded = false;
+ private ComponentSelector mSelector;
+ private Button mSaveApplyButton;
+ private int mContainerYOffset = 0;
+ private TypefaceHelperCache mTypefaceHelperCache;
+ private boolean mIsAnimating;
+ private Handler mHandler;
+ private View mBottomActionsLayout;
+
+ private String mSelectedTheme;
+ private String mAppliedBaseTheme;
+ private boolean mThemeChanging = false;
+ private boolean mAnimateContentIn = false;
+ private long mAnimateContentInDelay;
+ private String mThemeToApply;
+ private ArrayList mComponentsToApply;
+
+ ImageView mCustomBackground;
+
+ // Current system theme configuration as component -> pkgName
+ private Map<String, String> mCurrentTheme = new HashMap<String, String>();
+ private MutableLong mCurrentWallpaperCmpntId = new MutableLong(DEFAULT_COMPONENT_ID);
+
+ private boolean mIsPickingImage = false;
+ private boolean mRestartLoaderOnCollapse = false;
+ private boolean mActivityResuming = false;
+ private boolean mShowLockScreenWallpaper = false;
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ NotificationHijackingService.ensureEnabled(this);
+
+ if (savedInstanceState == null) {
+ handleIntent(getIntent());
+ }
+
+ mContainer = (PagerContainer) findViewById(R.id.pager_container);
+ mPager = (ThemeViewPager) findViewById(R.id.viewpager);
+
+ mPager.setOnClickListener(mPagerClickListener);
+ mAdapter = new ThemesAdapter();
+ mPager.setAdapter(mAdapter);
+
+ DisplayMetrics dm = getResources().getDisplayMetrics();
+ int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, dm);
+ mPager.setPageMargin(-margin / 2);
+ mPager.setOffscreenPageLimit(OFFSCREEN_PAGE_LIMIT);
+ mPager.setClipChildren(false);
+ mPager.setClipToPadding(false);
+
+ mPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ public void onPageSelected(int position) {
+ }
+
+ public void onPageScrolled(int position,
+ float positionOffset,
+ int positionOffsetPixels) {
+ }
+
+ public void onPageScrollStateChanged(int state) {
+ }
+ });
+
+ mSelector = (ComponentSelector) findViewById(R.id.component_selector);
+ mSelector.setOnOpenCloseListener(mOpenCloseListener);
+
+ mBottomActionsLayout = findViewById(R.id.bottom_actions_layout);
+
+ mSaveApplyButton = (Button) findViewById(R.id.save_apply_button);
+ mSaveApplyButton.setOnClickListener(mOnSaveApplyClicked);
+
+ mBottomActionsLayout.findViewById(R.id.shop_themes)
+ .setOnClickListener(mOnShopThemesClicked);
+
+ mTypefaceHelperCache = TypefaceHelperCache.getInstance();
+ mHandler = new Handler();
+ mCustomBackground = (ImageView) findViewById(R.id.custom_bg);
+ mAnimateContentIn = true;
+ mAnimateContentInDelay = 0;
+
+ mBottomActionsLayout.findViewById(R.id.per_app_theming).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (Settings.canDrawOverlays(ChooserActivity.this)) {
+ launchAppThemer();
+ } else {
+ requestSystemWindowPermission();
+ }
+ }
+ });
+
+ if (PreferenceUtils.getShowPerAppThemeNewTag(this)) {
+ View tag = mBottomActionsLayout.findViewById(R.id.new_tag);
+ if (tag != null) {
+ tag.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ public void showSaveApplyButton() {
+ if (mSaveApplyButton != null && mSaveApplyButton.getVisibility() != View.VISIBLE) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSaveApplyButton.setTranslationY(mSaveApplyButton.getMeasuredHeight());
+ mSaveApplyButton.setVisibility(View.VISIBLE);
+ mSaveApplyButton.animate()
+ .setDuration(ANIMATE_SAVE_APPLY_LAYOUT_DURATION)
+ .setInterpolator(
+ new DecelerateInterpolator(
+ ANIMATE_SAVE_APPLY_DECELERATE_INTERPOLATOR_FACTOR))
+ .translationY(-mSelector.getMeasuredHeight());
+ }
+ });
+ }
+ }
+
+ public void hideSaveApplyButton() {
+ if (mSaveApplyButton.getVisibility() != View.GONE) {
+ Animation anim = AnimationUtils.loadAnimation(this,
+ R.anim.component_selection_animate_out);
+ mSaveApplyButton.startAnimation(anim);
+ anim.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mSaveApplyButton.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ }
+ }
+
+ private void hideBottomActionsLayout() {
+ final ViewPropertyAnimator anim = mBottomActionsLayout.animate();
+ anim.alpha(0f).setDuration(ANIMATE_SHOP_THEMES_HIDE_DURATION);
+ anim.setListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBottomActionsLayout.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ });
+ }
+
+ private void showBottomActionsLayout() {
+ mBottomActionsLayout.setVisibility(View.VISIBLE);
+ final ViewPropertyAnimator anim = mBottomActionsLayout.animate();
+ anim.setListener(null);
+ anim.alpha(1f).setStartDelay(ThemeFragment.ANIMATE_DURATION)
+ .setDuration(ANIMATE_SHOP_THEMES_SHOW_DURATION);
+ }
+
+ private void lauchGetThemes() {
+ String playStoreUrl = getString(R.string.play_store_url);
+ String wikiUrl = getString(R.string.wiki_url);
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(playStoreUrl));
+
+ // Try to launch play store
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ startActivity(intent);
+ } else {
+ // If no play store, try to open wiki url
+ intent.setData(Uri.parse(wikiUrl));
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ startActivity(intent);
+ } else {
+ Toast.makeText(this, R.string.get_more_app_not_available,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ if (Utils.isRecentTaskHome(this)) {
+ mContainer.setAlpha(0f);
+ mBottomActionsLayout.setAlpha(0f);
+ mAnimateContentIn = true;
+ mAnimateContentInDelay = ANIMATE_CONTENT_DELAY;
+ }
+ handleIntent(intent);
+ }
+
+ private void handleIntent(Intent intent) {
+ String action = intent.getAction();
+ if ((Intent.ACTION_MAIN.equals(action) || ACTION_APPLY_THEME.equals(action))
+ && intent.hasExtra(EXTRA_PKGNAME)) {
+ if (intent.hasExtra(EXTRA_COMPONENTS)) {
+ mComponentsToApply = intent.getStringArrayListExtra(EXTRA_COMPONENTS);
+ } else {
+ mComponentsToApply = null;
+ }
+ mSelectedTheme = mComponentsToApply != null ?
+ PreferenceUtils.getAppliedBaseTheme(this) :
+ getSelectedTheme(intent.getStringExtra(EXTRA_PKGNAME));
+ if (mPager != null) {
+ startLoader(LOADER_ID_INSTALLED_THEMES);
+ if (mExpanded) {
+ int collapseDelay = ThemeFragment.ANIMATE_START_DELAY;
+ if (mSelector.isEnabled()) {
+ // onBackPressed() has all the necessary logic for collapsing the
+ // component selector, so we call it here.
+ onBackPressed();
+ collapseDelay += ThemeFragment.ANIMATE_DURATION;
+ }
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ collapse(false);
+ }
+ }, collapseDelay);
+ }
+ }
+
+ if (ACTION_APPLY_THEME.equals(action) &&
+ getCallingPackage() != null &&
+ PackageManager.PERMISSION_GRANTED ==
+ getPackageManager()
+ .checkPermission(Manifest.permission.WRITE_THEMES,
+ getCallingPackage())) {
+ mThemeToApply = intent.getStringExtra(EXTRA_PKGNAME);
+ }
+ } else if (ACTION_PICK_LOCK_SCREEN_WALLPAPER.equals(action)) {
+ mShowLockScreenWallpaper = true;
+ }
+ }
+
+ private String getSelectedTheme(String requestedTheme) {
+ String[] projection = { ThemesColumns.PRESENT_AS_THEME };
+ String selection = ThemesColumns.PKG_NAME + "=?";
+ String[] selectionArgs = { requestedTheme };
+
+ String selectedTheme = PreferenceUtils.getAppliedBaseTheme(this);
+
+ Cursor cursor = getContentResolver().query(ThemesColumns.CONTENT_URI,
+ projection, selection, selectionArgs, null);
+ if (cursor != null) {
+ if (cursor.getCount() > 0 && cursor.moveToFirst()) {
+ if (cursor.getInt(0) == 1) {
+ selectedTheme = requestedTheme;
+ }
+ }
+ cursor.close();
+ }
+ return selectedTheme;
+ }
+
+ private void setAnimatingStateAndScheduleFinish() {
+ mIsAnimating = true;
+ mContainer.setIsAnimating(true);
+ mHandler.postDelayed(new Runnable() {
+ public void run() {
+ mIsAnimating = false;
+ mContainer.setIsAnimating(false);
+ if (mRestartLoaderOnCollapse) {
+ mRestartLoaderOnCollapse = false;
+ startLoader(LOADER_ID_INSTALLED_THEMES);
+ }
+ }
+ }, FINISH_ANIMATION_DELAY);
+ if (mExpanded) {
+ hideBottomActionsLayout();
+ } else {
+ showBottomActionsLayout();
+ }
+ }
+
+ private void setCustomBackground(final ImageView iv, final boolean animate) {
+ final Context context = ChooserActivity.this;
+ iv.post(new Runnable() {
+ @Override
+ public void run() {
+ Bitmap tmpBmp;
+ try {
+ tmpBmp = Utils.getRegularWallpaperBitmap(context);
+ } catch (Throwable e) {
+ Log.w(TAG, "Failed to retrieve wallpaper", e);
+ tmpBmp = null;
+ }
+ // Show the grid background if no wallpaper is set.
+ // Note: no wallpaper is actually a 1x1 pixel black bitmap
+ if (tmpBmp == null || tmpBmp.getWidth() <= 1 || tmpBmp.getHeight() <= 1) {
+ iv.setImageResource(R.drawable.bg_grid);
+ // We need to change the ScaleType to FIT_XY otherwise the background is cut
+ // off a bit at the bottom.
+ iv.setScaleType(ImageView.ScaleType.FIT_XY);
+ return;
+ }
+
+ // Since we are applying a blur, we can afford to scale the bitmap down and use a
+ // smaller blur radius.
+ Bitmap inBmp = Bitmap.createScaledBitmap(tmpBmp, tmpBmp.getWidth() / 4,
+ tmpBmp.getHeight() / 4, false);
+ Bitmap outBmp = Bitmap.createBitmap(inBmp.getWidth(), inBmp.getHeight(),
+ Bitmap.Config.ARGB_8888);
+
+ // Blur the original bitmap
+ RenderScript rs = RenderScript.create(context);
+ ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
+ Allocation tmpIn = Allocation.createFromBitmap(rs, inBmp);
+ Allocation tmpOut = Allocation.createFromBitmap(rs, outBmp);
+ theIntrinsic.setRadius(5.0f);
+ theIntrinsic.setInput(tmpIn);
+ theIntrinsic.forEach(tmpOut);
+ tmpOut.copyTo(outBmp);
+
+ // Create a bitmap drawable and use a color matrix to de-saturate the image
+ BitmapDrawable[] layers = new BitmapDrawable[2];
+ layers[0] = new BitmapDrawable(getResources(), tmpBmp);
+ layers[1] = new BitmapDrawable(getResources(), outBmp);
+ ColorMatrix cm = new ColorMatrix();
+ cm.setSaturation(0);
+ Paint p = layers[0].getPaint();
+ p.setColorFilter(new ColorMatrixColorFilter(cm));
+ p = layers[1].getPaint();
+ p.setColorFilter(new ColorMatrixColorFilter(cm));
+ TransitionDrawable d = new TransitionDrawable(layers);
+
+ // All done
+ iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ if (!animate) {
+ iv.setImageDrawable(layers[1]);
+ } else {
+ iv.setImageDrawable(d);
+ }
+ }
+ });
+ }
+
+ /**
+ * Disable the ViewPager while a theme change is occuring
+ */
+ public void themeChangeStart() {
+ lockPager();
+ mThemeChanging = true;
+ ThemeFragment f = getCurrentFragment();
+ if (f != null) {
+ mAppliedBaseTheme = f.getThemePackageName();
+ PreferenceUtils.setAppliedBaseTheme(this, mAppliedBaseTheme);
+ }
+ }
+
+ /**
+ * Re-enable the ViewPager and update the "My theme" fragment if available
+ */
+ public void themeChangeEnd(boolean isSuccess) {
+ mThemeChanging = false;
+ ThemeFragment f = getCurrentFragment();
+ 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
+ if (!(f instanceof MyThemeFragment)) {
+ mAdapter = new ThemesAdapter();
+ mPager.setAdapter(mAdapter);
+ }
+ if (!isSuccess) {
+ mAppliedBaseTheme = null;
+ }
+ startLoader(LOADER_ID_APPLIED);
+ }
+ unlockPager();
+ }
+
+ public void lockPager() {
+ mPager.setScrollingEnabled(false);
+ }
+
+ public void unlockPager() {
+ mPager.setScrollingEnabled(true);
+ }
+
+ public ComponentSelector getComponentSelector() {
+ return mSelector;
+ }
+
+ public void showComponentSelector(String component, String selectedPkgName,
+ long selectedCmpntId, View v) {
+ if (component != null) {
+ final Resources res = getResources();
+ int itemsPerPage = res.getInteger(R.integer.default_items_per_page);
+ int height = res.getDimensionPixelSize(R.dimen.component_selection_cell_height);
+ if (MODIFIES_BOOT_ANIM.equals(component)) {
+ itemsPerPage = res.getInteger(R.integer.bootani_items_per_page);
+ height = res.getDimensionPixelSize(
+ R.dimen.component_selection_cell_height_boot_anim);
+ } else if (MODIFIES_ALARMS.equals(component) ||
+ MODIFIES_NOTIFICATIONS.equals(component) ||
+ MODIFIES_RINGTONES.equals(component)) {
+ itemsPerPage = 2;
+ height = res.getDimensionPixelSize(
+ R.dimen.component_selection_cell_height_sounds);
+ }
+ if (mSaveApplyButton.getVisibility() == View.VISIBLE) {
+ if (mSaveApplyButton.getTranslationY() + height != 0) {
+ mSaveApplyButton.animate()
+ .translationY(-height)
+ .setInterpolator(
+ new AccelerateDecelerateInterpolator())
+ .setDuration(ANIMATE_SAVE_APPLY_LAYOUT_DURATION);
+ }
+ }
+ mSelector.show(component, selectedPkgName, selectedCmpntId, itemsPerPage, height);
+
+ // determine if we need to shift the cards up
+ int[] coordinates = new int[2];
+ v.getLocationOnScreen(coordinates);
+ final int top = coordinates[1];
+ final int bottom = coordinates[1] + v.getHeight();
+ final int statusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
+ int selectorTop = getWindowManager().getDefaultDisplay().getHeight() - height;
+ if (bottom > selectorTop) {
+ slideContentIntoView(bottom - selectorTop, height);
+ } else if (top < statusBarHeight) {
+ slideContentIntoView(top - statusBarHeight, height);
+ }
+ }
+ }
+
+ public void expand() {
+ if (!mExpanded && !mIsAnimating) {
+ mExpanded = true;
+ mContainer.setClickable(false);
+ mContainer.expand();
+ ThemeFragment f = getCurrentFragment();
+ if (f != null) {
+ f.expand();
+ }
+ setAnimatingStateAndScheduleFinish();
+ }
+ }
+
+ public void collapse(final boolean applyTheme) {
+ mExpanded = false;
+ final ThemeFragment f = getCurrentFragment();
+ if (f != null) {
+ f.fadeOutCards(new Runnable() {
+ public void run() {
+ mContainer.collapse();
+ f.collapse(applyTheme);
+ }
+ });
+ }
+ setAnimatingStateAndScheduleFinish();
+ }
+
+ 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;
+ }
+
+ public void uninstallTheme(String pkgName) {
+ PackageManager pm = getPackageManager();
+ pm.deletePackage(pkgName, new PackageDeleteObserver(), PackageManager.DELETE_ALL_USERS);
+ }
+
+ private void slideContentIntoView(int yDelta, int selectorHeight) {
+ ThemeFragment f = getCurrentFragment();
+ if (f != null) {
+ final int offset = getResources().getDimensionPixelSize(R.dimen.content_offset_padding);
+ if (yDelta > 0) {
+ yDelta += offset;
+ } else {
+ yDelta -= offset;
+ }
+ f.slideContentIntoView(yDelta, selectorHeight);
+ mContainerYOffset = yDelta;
+ }
+ }
+
+ private void slideContentBack(final int yDelta) {
+ ThemeFragment f = getCurrentFragment();
+ if (f != null) {
+ f.slideContentBack(yDelta);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ setCustomBackground(mCustomBackground, mAnimateContentIn);
+ // clear out any notifications that are being displayed.
+ NotificationHelper.cancelNotifications(this);
+
+ ThemeManager tm = ThemeManager.getInstance(this);
+ mThemeChanging = tm.isThemeApplying();
+
+ if (!mIsPickingImage) {
+ startLoader(LOADER_ID_APPLIED);
+ } else {
+ mIsPickingImage = false;
+ }
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
+ registerReceiver(mWallpaperChangeReceiver, filter);
+ }
+
+ @Override
+ public void onBackPressed() {
+ final ThemeFragment f = getCurrentFragment();
+ if (mSelector.isEnabled()) {
+ mSelector.hide();
+ if (mContainerYOffset != 0) {
+ slideContentBack(-mContainerYOffset);
+ mContainerYOffset = 0;
+ }
+ if (f != null) f.fadeInCards();
+ if (mShowLockScreenWallpaper) {
+ mShowLockScreenWallpaper = false;
+ mSelector.resetComponentType();
+ }
+ } else if (mExpanded) {
+ if (mIsAnimating) {
+ return;
+ }
+
+ if (mSaveApplyButton.getVisibility() == View.VISIBLE) {
+ hideSaveApplyButton();
+ if (f != null) f.clearChanges();
+ }
+ collapse(false);
+ } else {
+ if (f != null && f.isShowingConfirmCancelOverlay()) {
+ f.hideConfirmCancelOverlay();
+ } else if (f != null && f.isShowingCustomizeResetLayout()) {
+ f.hideCustomizeResetLayout();
+ } else {
+ super.onBackPressed();
+ }
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ unregisterReceiver(mWallpaperChangeReceiver);
+ ThemeFragment f = getCurrentFragment();
+ if (f != null) {
+ mSelectedTheme = f.getThemePackageName();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (mTypefaceHelperCache.getTypefaceCount() <= 0) {
+ new TypefacePreloadTask().execute();
+ }
+ 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);
+ showSaveApplyButton();
+ }
+ }
+ } 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);
+ showSaveApplyButton();
+ }
+ }
+ } else if (requestCode == REQUEST_SYSTEM_WINDOW_PERMISSION) {
+ if (Settings.canDrawOverlays(this)) {
+ launchAppThemer();
+ }
+ }
+ }
+
+ private void animateContentIn() {
+ Drawable d = mCustomBackground.getDrawable();
+ if (d instanceof TransitionDrawable) {
+ ((TransitionDrawable) d).startTransition((int) ANIMATE_CONTENT_IN_BLUR_DURATION);
+ }
+
+ if (!mShowLockScreenWallpaper) {
+ AnimatorSet set = new AnimatorSet();
+ set.play(ObjectAnimator.ofFloat(mContainer, "alpha", 0f, 1f)
+ .setDuration(ANIMATE_CONTENT_IN_ALPHA_DURATION))
+ .with(ObjectAnimator.ofFloat(mContainer, "scaleX", 2f, 1f)
+ .setDuration(ANIMATE_CONTENT_IN_SCALE_DURATION))
+ .with(ObjectAnimator.ofFloat(mContainer, "scaleY", 2f, 1f)
+ .setDuration(ANIMATE_CONTENT_IN_SCALE_DURATION));
+ set.setStartDelay(mAnimateContentInDelay);
+ set.start();
+ mBottomActionsLayout.setAlpha(0f);
+ mBottomActionsLayout.animate().alpha(1f).setStartDelay(mAnimateContentInDelay)
+ .setDuration(ANIMATE_CONTENT_IN_ALPHA_DURATION);
+ } else {
+ mContainer.setAlpha(0f);
+ mContainer.setVisibility(View.GONE);
+ }
+ mAnimateContentIn = false;
+ }
+
+ private View.OnClickListener mPagerClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ ThemeFragment f = getCurrentFragment();
+ if (f != null && !mThemeChanging) {
+ f.performClick(mPager.isClickedOnContent());
+ }
+ }
+ };
+
+ private BroadcastReceiver mWallpaperChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mCustomBackground != null) setCustomBackground(mCustomBackground, false);
+ }
+ };
+
+ private ComponentSelector.OnOpenCloseListener mOpenCloseListener = new ComponentSelector.OnOpenCloseListener() {
+ @Override
+ public void onSelectorOpened() {
+ }
+
+ @Override
+ public void onSelectorClosed() {
+ }
+
+ @Override
+ public void onSelectorClosing() {
+ ThemeFragment f = getCurrentFragment();
+ if (f != null && f.componentsChanged()
+ && mSaveApplyButton.getVisibility() == View.VISIBLE) {
+ mSaveApplyButton.animate()
+ .translationY(0)
+ .setInterpolator(new DecelerateInterpolator())
+ .setDuration(ANIMATE_SAVE_APPLY_LAYOUT_DURATION);
+ }
+ }
+ };
+
+ private ThemeFragment getCurrentFragment() {
+ // instantiateItem will return the fragment if it already exists and not instantiate it,
+ // which should be the case for the current fragment.
+ ThemeFragment f;
+ try {
+ f = (mAdapter == null || mPager == null || mAdapter.getCount() <= 0) ? null :
+ (ThemeFragment) mAdapter.instantiateItem(mPager, mPager.getCurrentItem());
+ } catch (Exception e) {
+ f = null;
+ Log.e(TAG, "Unable to get current fragment", e);
+ }
+ return f;
+ }
+
+ private void populateCurrentTheme(Cursor c) {
+ c.moveToPosition(-1);
+ //Default to first wallpaper
+ mCurrentWallpaperCmpntId.value = DEFAULT_COMPONENT_ID;
+ // clear out the previous map
+ mCurrentTheme.clear();
+ while(c.moveToNext()) {
+ int mixkeyIdx = c.getColumnIndex(ThemesContract.MixnMatchColumns.COL_KEY);
+ int pkgIdx = c.getColumnIndex(ThemesContract.MixnMatchColumns.COL_VALUE);
+ int cmpntIdIdx = c.getColumnIndex(ThemesContract.MixnMatchColumns.COL_COMPONENT_ID);
+ String mixkey = c.getString(mixkeyIdx);
+ String component = ThemesContract.MixnMatchColumns.mixNMatchKeyToComponent(mixkey);
+ String pkg = c.getString(pkgIdx);
+ mCurrentTheme.put(component, pkg);
+ if (TextUtils.equals(component, ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN)) {
+ mCurrentTheme.remove(ThemesColumns.MODIFIES_LOCKSCREEN);
+ }
+ if (TextUtils.equals(component, ThemesColumns.MODIFIES_LOCKSCREEN)) {
+ mCurrentTheme.remove(ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN);
+ }
+ if (cmpntIdIdx >= 0 && TextUtils.equals(component, ThemesColumns.MODIFIES_LAUNCHER)) {
+ mCurrentWallpaperCmpntId.value = c.getLong(cmpntIdIdx);
+ }
+ }
+ }
+
+ private View.OnClickListener mOnShopThemesClicked = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent();
+ intent.setClassName(THEME_STORE_PACKAGE, THEME_STORE_ACTIVITY);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ lauchGetThemes();
+ }
+ }
+ };
+
+ private View.OnClickListener mOnSaveApplyClicked = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mIsAnimating) return;
+ hideSaveApplyButton();
+ mContainer.setClickable(false);
+ final ThemeFragment f = getCurrentFragment();
+ if (mSelector.isEnabled()) {
+ mSelector.hide();
+ if (mContainerYOffset != 0) {
+ slideContentBack(-mContainerYOffset);
+ mContainerYOffset = 0;
+ }
+ if (f != null) f.fadeInCards();
+ if (mShowLockScreenWallpaper) {
+ mShowLockScreenWallpaper = false;
+ mSelector.resetComponentType();
+ }
+ }
+
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ collapse(true);
+ }
+ }, ONCLICK_SAVE_APPLY_FINISH_ANIMATION_DELAY);
+ }
+ };
+
+ private <T> void startLoader(int loaderId) {
+ final LoaderManager manager = getSupportLoaderManager();
+ final Loader<T> loader = manager.getLoader(loaderId);
+ if (loader != null) {
+ manager.restartLoader(loaderId, null, this);
+ } else {
+ manager.initLoader(loaderId, null, this);
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (mThemeChanging) return;
+
+ if (mExpanded && !mActivityResuming) {
+ mRestartLoaderOnCollapse = true;
+ return;
+ }
+
+ switch (loader.getId()) {
+ case LOADER_ID_INSTALLED_THEMES:
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ int selectedThemeIndex = -1;
+ if (TextUtils.isEmpty(mSelectedTheme)) mSelectedTheme = mAppliedBaseTheme;
+ while(data.moveToNext()) {
+ if (mSelectedTheme.equals(data.getString(
+ data.getColumnIndex(ThemesColumns.PKG_NAME)))) {
+ // we need to add one here since the first card is "My theme"
+ selectedThemeIndex = data.getPosition();
+ mSelectedTheme = null;
+ break;
+ }
+ }
+ data.moveToFirst();
+ mAdapter.swapCursor(data);
+ mAdapter.notifyDataSetChanged();
+ if (selectedThemeIndex >= 0) {
+ mPager.setCurrentItem(selectedThemeIndex, false);
+
+ if (mThemeToApply != null) {
+ ThemeFragment f = getCurrentFragment();
+ f.applyThemeWhenPopulated(mThemeToApply, mComponentsToApply);
+ mThemeToApply = null;
+ }
+ }
+ if (mAnimateContentIn) animateContentIn();
+ mActivityResuming = true;
+ break;
+ case LOADER_ID_APPLIED:
+ startLoader(LOADER_ID_INSTALLED_THEMES);
+ populateCurrentTheme(data);
+ break;
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ switch (loader.getId()) {
+ case LOADER_ID_INSTALLED_THEMES:
+ mAdapter.swapCursor(null);
+ mAdapter.notifyDataSetChanged();
+ break;
+ }
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_ID_INSTALLED_THEMES:
+ mAppliedBaseTheme = PreferenceUtils.getAppliedBaseTheme(this);
+ break;
+ case LOADER_ID_APPLIED:
+ //TODO: Mix n match query should only be done once
+ break;
+ }
+ return CursorLoaderHelper.chooserActivityCursorLoader(this, id, mAppliedBaseTheme);
+ }
+
+ public Map<String, String> getSelectedComponentsMap() {
+ return getCurrentFragment().getSelectedComponentsMap();
+ }
+
+ public class ThemesAdapter extends NewFragmentStatePagerAdapter {
+ private ArrayList<String> mInstalledThemes;
+ private String mAppliedThemeTitle;
+ private String mAppliedThemeAuthor;
+ private HashMap<String, Integer> mRepositionedFragments;
+
+ public ThemesAdapter() {
+ super(getSupportFragmentManager());
+ mRepositionedFragments = new HashMap<String, Integer>();
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ ThemeFragment f = null;
+ MutableLong wallpaperCmpntId;
+ if (mInstalledThemes != null) {
+ final String pkgName = mInstalledThemes.get(position);
+ if (pkgName.equals(mAppliedBaseTheme)) {
+ f = MyThemeFragment.newInstance(mAppliedBaseTheme, mAppliedThemeTitle,
+ mAppliedThemeAuthor, mAnimateContentIn, mShowLockScreenWallpaper);
+ wallpaperCmpntId = mCurrentWallpaperCmpntId;
+ } else {
+ f = ThemeFragment.newInstance(pkgName, mAnimateContentIn);
+ wallpaperCmpntId = new MutableLong(DEFAULT_COMPONENT_ID);
+ }
+ f.setCurrentTheme(mCurrentTheme, wallpaperCmpntId);
+ }
+ return f;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (mInstalledThemes != null) {
+ final String pkgName = mInstalledThemes.get(position);
+ return pkgName.hashCode();
+ }
+ return 0;
+ }
+
+ @Override
+ public int getItemPosition(Object object) {
+ final ThemeFragment f = (ThemeFragment) object;
+ final String pkgName = f != null ? f.getThemePackageName() : null;
+ if (pkgName != null && mRepositionedFragments.containsKey(pkgName)) {
+ final int position = mRepositionedFragments.get(pkgName);
+ mRepositionedFragments.remove(pkgName);
+ return position;
+ }
+ return super.getItemPosition(object);
+ }
+
+ /**
+ * The first card should be the user's currently applied theme components so we
+ * will always return at least 1 or mCursor.getCount() + 1
+ * @return
+ */
+ public int getCount() {
+ return mInstalledThemes == null ? 0 : mInstalledThemes.size();
+ }
+
+ public void swapCursor(Cursor c) {
+ if (c != null && c.getCount() != 0) {
+ ArrayList<String> previousOrder = mInstalledThemes == null ? null
+ : new ArrayList<String>(mInstalledThemes);
+ mInstalledThemes = new ArrayList<String>(c.getCount());
+ mRepositionedFragments.clear();
+ c.moveToPosition(-1);
+ while (c.moveToNext()) {
+ final int pkgIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+ final String pkgName = c.getString(pkgIdx);
+ if (pkgName.equals(mAppliedBaseTheme)) {
+ final int titleIdx = c.getColumnIndex(ThemesColumns.TITLE);
+ final int authorIdx = c.getColumnIndex(ThemesColumns.AUTHOR);
+ mAppliedThemeTitle = c.getString(titleIdx);
+ mAppliedThemeAuthor = c.getString(authorIdx);
+ }
+ mInstalledThemes.add(pkgName);
+
+ // track any themes that may have changed position
+ if (previousOrder != null && previousOrder.contains(pkgName)) {
+ int index = previousOrder.indexOf(pkgName);
+ if (index != c.getPosition()) {
+ mRepositionedFragments.put(pkgName, c.getPosition());
+ }
+ } else {
+ mRepositionedFragments.put(pkgName, c.getPosition());
+ }
+ }
+ // check if any themes are no longer in the new list
+ if (previousOrder != null) {
+ for (String pkgName : previousOrder) {
+ if (!mInstalledThemes.contains(pkgName)) {
+ mRepositionedFragments.put(pkgName, POSITION_NONE);
+ }
+ }
+ }
+ } else {
+ mInstalledThemes = null;
+ }
+ }
+
+ public void removeTheme(String pkgName) {
+ if (mInstalledThemes == null) return;
+
+ if (mInstalledThemes.contains(pkgName)) {
+ final int count = mInstalledThemes.size();
+ final int index = mInstalledThemes.indexOf(pkgName);
+ // reposition all the fragments after the one being removed
+ for (int i = index + 1; i < count; i++) {
+ mRepositionedFragments.put(mInstalledThemes.get(i), i - 1);
+ }
+ // Now remove this theme and add it to mRepositionedFragments with POSITION_NONE
+ mInstalledThemes.remove(pkgName);
+ mRepositionedFragments.put(pkgName, POSITION_NONE);
+ // now we can call notifyDataSetChanged()
+ notifyDataSetChanged();
+ }
+ }
+ }
+
+ private class TypefacePreloadTask extends AsyncTask {
+
+ @Override
+ protected Object doInBackground(Object[] params) {
+ String[] projection = { ThemesColumns.PKG_NAME };
+ String selection = ThemesColumns.MODIFIES_FONTS + "=?";
+ String[] selectionArgs = { "1" };
+ Cursor c = getContentResolver().query(ThemesColumns.CONTENT_URI, projection, selection,
+ selectionArgs, null);
+ if (c != null) {
+ while (c.moveToNext()) {
+ mTypefaceHelperCache.getHelperForTheme(ChooserActivity.this, c.getString(0));
+ }
+ c.close();
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Internal delete callback from the system
+ */
+ class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
+ public void packageDeleted(final String packageName, int returnCode) throws RemoteException {
+ if (returnCode == PackageManager.DELETE_SUCCEEDED) {
+ Log.d(TAG, "Delete succeeded");
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAdapter.removeTheme(packageName);
+ }
+ });
+ } else {
+ Log.e(TAG, "Delete failed with returnCode " + returnCode);
+ }
+ }
+ }
+
+ public void expandContentAndAnimateLockScreenCardIn() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ expand();
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ AnimatorSet set = new AnimatorSet();
+ set.play(ObjectAnimator.ofFloat(mContainer, "alpha", 0f, 1f)
+ .setDuration(ANIMATE_CARDS_IN_DURATION));
+ set.setStartDelay(mAnimateContentInDelay);
+ set.start();
+ mContainer.setVisibility(View.VISIBLE);
+ getCurrentFragment().showLockScreenCard();
+ }
+ }, ANIMATE_CARDS_IN_DURATION);
+ }
+ });
+ }
+
+ private void launchAppThemer() {
+ PreferenceUtils.setShowPerAppThemeNewTag(ChooserActivity.this, false);
+ Intent intent = new Intent(ChooserActivity.this, PerAppThemingWindow.class);
+ startService(intent);
+ finish();
+ }
+
+ private void requestSystemWindowPermission() {
+ Intent intent = new Intent (Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
+ Uri.parse("package:" + getPackageName()));
+ startActivityForResult(intent, REQUEST_SYSTEM_WINDOW_PERMISSION);
+ }
+}
diff --git a/src/org/cyanogenmod/theme/chooser2/ComponentCardView.java b/src/org/cyanogenmod/theme/chooser2/ComponentCardView.java
new file mode 100644
index 0000000..f9578c7
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/ComponentCardView.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOverlay;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class ComponentCardView extends LinearLayout {
+ public static final int CARD_FADE_DURATION = 300;
+
+ private static final float SEMI_OPAQUE_ALPHA = 0.4f;
+
+ protected TextView mLabel;
+ protected View mEmptyView;
+ protected View mContentView;
+
+ // Expanded Padding
+ int mExpandPadLeft;
+ int mExpandPadTop;
+ int mExpandPadRight;
+ int mExpandPadBottom;
+
+ // The background drawable is returning an alpha of 0 regardless of what we set it to
+ // so this will help us keep track of what the drawable's alpha is at.
+ private int mBackgroundAlpha = 255;
+
+ public ComponentCardView(Context context) {
+ this(context, null);
+ }
+
+ public ComponentCardView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ComponentCardView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setEnabled(false);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mLabel = (TextView) findViewById(R.id.label);
+
+ Resources r = getContext().getResources();
+ mExpandPadLeft =
+ (int) r.getDimension(R.dimen.card_padding_left_right) + getPaddingLeft();
+ mExpandPadTop =
+ (int) r.getDimension(R.dimen.card_padding_top) + getPaddingTop();
+ mExpandPadRight =
+ (int) r.getDimension(R.dimen.card_padding_left_right) + getPaddingRight();
+ mExpandPadBottom =
+ (int) r.getDimension(R.dimen.card_padding_bottom) + getPaddingBottom();
+ mEmptyView = findViewById(R.id.empty);
+ mContentView = findViewById(R.id.content);
+ }
+
+ public void expand(boolean showLabel) {
+ setEnabled(true);
+ TransitionDrawable bg = null;
+ if (getBackground() instanceof TransitionDrawable) {
+ bg = (TransitionDrawable) getBackground();
+ }
+ if (bg != null) {
+ Rect paddingRect = new Rect();
+ bg.getPadding(paddingRect);
+ }
+
+ setPadding(mExpandPadLeft, 0, mExpandPadRight, mExpandPadBottom);
+
+ if (mLabel != null) {
+ mLabel.setAlpha(showLabel ? 1f : 0f);
+ mLabel.setVisibility(View.VISIBLE);
+ }
+ }
+
+ public void animateExpand() {
+ if (getBackground() instanceof TransitionDrawable) {
+ TransitionDrawable background = (TransitionDrawable) getBackground();
+ if (mLabel != null) {
+ mLabel.setVisibility(View.VISIBLE);
+ mLabel.setAlpha(0f);
+ mLabel.animate().alpha(1f).setDuration(CARD_FADE_DURATION).start();
+ }
+ background.startTransition(CARD_FADE_DURATION);
+ }
+ }
+
+ public void collapse() {
+ setEnabled(false);
+ if (mLabel != null) {
+ mLabel.setVisibility(View.GONE);
+ }
+ setPadding(0, 0, 0, 0);
+ }
+
+ public void animateFadeOut() {
+ if (mLabel != null) {
+ mLabel.animate().alpha(0f).setDuration(CARD_FADE_DURATION);
+ }
+
+ if (getBackground() instanceof TransitionDrawable) {
+ TransitionDrawable background = (TransitionDrawable) getBackground();
+ background.reverseTransition(CARD_FADE_DURATION);
+ }
+ }
+
+ /**
+ * Animates the card background and the title to 20% opacity.
+ */
+ public void animateCardFadeOut() {
+ this.animate().alpha(SEMI_OPAQUE_ALPHA).setDuration(CARD_FADE_DURATION);
+ }
+
+ /**
+ * Animates the card background and the title back to full opacity.
+ */
+ public void animateCardFadeIn() {
+ this.animate().alpha(1f).setDuration(CARD_FADE_DURATION);
+ }
+
+ /**
+ * Animates a change in the content of the card
+ * @param v View in card to animate
+ * @param overlay Drawable to animate as a ViewOverlay
+ * @param duration Duration of animation
+ */
+ public void animateContentChange(View v, final Drawable overlay, long duration) {
+ final ViewOverlay viewOverlay = this.getOverlay();
+ viewOverlay.add(overlay);
+ final int x = getRelativeLeft(v);
+ final int y = getRelativeTop(v);
+ final int width = v.getWidth();
+ final int height = v.getHeight();
+ overlay.setBounds(x, y, x + v.getWidth(), y + v.getHeight());
+
+ final ValueAnimator overlayAnimator = ValueAnimator.ofFloat(1f, 0f);
+ overlayAnimator.setDuration(duration);
+ overlayAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float value = (Float) animation.getAnimatedValue();
+ overlay.setAlpha((int) (255 * value));
+ int newWidth = (int) (value * width);
+ int newHeight = (int) (value * height);
+ int dw = (width - newWidth) / 2;
+ int dh = (height - newHeight) / 2;
+ overlay.setBounds(x + dw, y + dh, x + dw + newWidth, y + dh + newHeight);
+ }
+ });
+ overlayAnimator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Clear out the ViewOverlay now that we are done animating
+ viewOverlay.clear();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {}
+ });
+
+ AnimatorSet set = new AnimatorSet();
+ set.play(ObjectAnimator.ofFloat(v, "alpha", 0f, 1f))
+ .with(ObjectAnimator.ofFloat(v, "scaleX", 0f, 1f))
+ .with(ObjectAnimator.ofFloat(v, "scaleY", 0f, 1f));
+ set.setDuration(duration);
+
+ set.start();
+ overlayAnimator.start();
+ }
+
+ public void setEmptyViewEnabled(boolean enabled) {
+ if (mEmptyView != null) {
+ mEmptyView.setVisibility(enabled ? View.VISIBLE : View.GONE);
+ }
+ if (mContentView != null) {
+ mContentView.setVisibility(enabled ? View.INVISIBLE : View.VISIBLE);
+ }
+ }
+
+ public boolean isShowingEmptyView() {
+ return mEmptyView != null && mEmptyView.getVisibility() == View.VISIBLE;
+ }
+
+ private int getRelativeTop(View v) {
+ if (v.getParent() == this) {
+ return v.getTop();
+ } else {
+ return v.getTop() + ((View) v.getParent()).getTop();
+ }
+ }
+
+ private int getRelativeLeft(View v) {
+ if (v.getParent() == this) {
+ return v.getLeft();
+ } else {
+ return v.getLeft() + ((View) v.getParent()).getLeft();
+ }
+ }
+}
diff --git a/src/org/cyanogenmod/theme/chooser2/ComponentSelector.java b/src/org/cyanogenmod/theme/chooser2/ComponentSelector.java
new file mode 100644
index 0000000..178c9b6
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/ComponentSelector.java
@@ -0,0 +1,934 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
+import android.media.MediaPlayer;
+import android.media.RingtoneManager;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
+import android.widget.ComponentSelectorLinearLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.cyanogenmod.theme.util.AudioUtils;
+import org.cyanogenmod.theme.util.CursorLoaderHelper;
+import org.cyanogenmod.theme.util.ThemedTypefaceHelper;
+import org.cyanogenmod.theme.util.TypefaceHelperCache;
+import org.cyanogenmod.theme.util.Utils;
+
+import cyanogenmod.providers.ThemesContract;
+import cyanogenmod.providers.ThemesContract.PreviewColumns;
+import cyanogenmod.providers.ThemesContract.ThemesColumns;
+
+import java.util.Map;
+
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_ALARMS;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_BOOT_ANIM;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_LAUNCHER;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_LOCKSCREEN;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_NOTIFICATIONS;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_OVERLAYS;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_RINGTONES;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_STATUS_BAR;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_NAVIGATION_BAR;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_ICONS;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_FONTS;
+
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_STATUS_BAR;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_FONT;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_ICONS;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_WALLPAPER;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_NAVIGATION_BAR;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_LOCKSCREEN;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_STYLE;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_BOOT_ANIMATION;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_RINGTONE;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_NOTIFICATION;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_ALARM;
+
+public class ComponentSelector extends LinearLayout
+ implements LoaderManager.LoaderCallbacks<Cursor> {
+ private static final String TAG = ComponentSelector.class.getSimpleName();
+
+ public static final boolean DEBUG_SELECTOR = false;
+
+ public static final String EXTERNAL_WALLPAPER = "external";
+
+ public static final String MOD_LOCK = "mod_lock";
+
+ private static final int EXTRA_WALLPAPER_COMPONENTS = 2;
+
+ private static final int EXTRA_LOCK_SCREEN_WALLPAPER_COMPONENTS = 3;
+
+ protected static final long DEFAULT_COMPONENT_ID = 0;
+
+ private Context mContext;
+ private LayoutInflater mInflater;
+ private ComponentSelectorLinearLayout mContent;
+ private LinearLayout.LayoutParams mItemParams;
+ private LinearLayout.LayoutParams mSoundItemParams =
+ new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0, 1.0f);
+
+ private String mComponentType;
+ private int mBatteryStyle;
+ private int mItemsPerPage;
+ private String mAppliedComponentPkgName;
+ private String mSelectedComponentPkgName;
+ private long mSelectedComponentId;
+
+ // animations for bringing selector in and out of view
+ private Animation mAnimateIn;
+ private Animation mAnimateOut;
+
+ private OnItemClickedListener mListener;
+
+ private OnOpenCloseListener mOpenCloseListener;
+
+ private MediaPlayer mMediaPlayer;
+ private ImageView mCurrentPlayPause;
+
+ private TypefaceHelperCache mTypefaceCache;
+
+ private ThemesObserver mThemesObserver;
+
+ public static final String IS_LIVE_LOCK_SCREEN_VIEW = "is_live_lock_screen_view";
+ private View mPrevLockScreenView;
+
+ public ComponentSelector(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ComponentSelector);
+ mItemsPerPage = a.getInt(R.styleable.ComponentSelector_itemsPerPage,
+ context.getResources().getInteger(R.integer.default_items_per_page));
+ a.recycle();
+
+ mContext = context;
+ mInflater = LayoutInflater.from(context);
+ // TODO: Load from settings once available
+ mBatteryStyle = 0; /*Settings.System.getInt(context.getContentResolver(),
+ Settings.System.STATUS_BAR_BATTERY_STYLE, 0);*/
+
+ mAnimateIn = AnimationUtils.loadAnimation(mContext,
+ R.anim.component_selection_animate_in);
+ mAnimateOut = AnimationUtils.loadAnimation(mContext,
+ R.anim.component_selection_animate_out);
+ mAnimateIn.setAnimationListener(new AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ if (mOpenCloseListener != null) mOpenCloseListener.onSelectorOpened();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ mAnimateOut.setAnimationListener(new AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ if (mOpenCloseListener != null) mOpenCloseListener.onSelectorClosing();
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ mMediaPlayer = new MediaPlayer();
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ if (mCurrentPlayPause != null) {
+ mCurrentPlayPause.setImageResource(R.drawable.media_sound_selector_preview);
+ mCurrentPlayPause = null;
+ }
+ }
+ });
+ mTypefaceCache = TypefaceHelperCache.getInstance();
+ mThemesObserver = new ThemesObserver();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mContent = (ComponentSelectorLinearLayout) findViewById(R.id.content);
+ setEnabled(false);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mThemesObserver.register();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mThemesObserver.unregister();
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ // Window visibility transitions as follows
+ // VISIBLE -> INVISIBLE -> GONE
+ // GONE -> INVISIBLE -> VISIBLE
+ if (visibility == View.GONE) {
+ mThemesObserver.unregister();
+ } else if (visibility == View.VISIBLE) {
+ mThemesObserver.register();
+ }
+ }
+
+ public void resetComponentType() {
+ mComponentType = null;
+ }
+
+ public void setComponentType(String component) {
+ setComponentType(component, null, DEFAULT_COMPONENT_ID);
+ }
+
+ public void setComponentType(String component, String selectedPkgName) {
+ setComponentType(component, null, DEFAULT_COMPONENT_ID);
+ }
+
+ public void setComponentType(String component, String selectedPkgName,
+ long selectedComponentId) {
+ mSelectedComponentPkgName = selectedPkgName;
+ mSelectedComponentId = selectedComponentId;
+ if (mComponentType == null || !mComponentType.equals(component)) {
+ mContent.removeAllViews();
+ mComponentType = component;
+ ((FragmentActivity) mContext).getSupportLoaderManager().restartLoader(
+ getLoaderIdFromComponent(component), null, this);
+ } else if (mComponentType != null) {
+ int count = mContent.getChildCount();
+ final Resources res = getResources();
+ boolean highlightTitle;
+ Map<String, String> selectedComponents = null;
+ if (mComponentType.equals(MODIFIES_LOCKSCREEN)) {
+ selectedComponents = ((ChooserActivity)mContext)
+ .getSelectedComponentsMap();
+ }
+ for (int i = 0; i < count; i++) {
+ final View child = mContent.getChildAt(i);
+ final TextView tv = (TextView) child.findViewById(R.id.title);
+ // The child itself may have the tag, or in the case of sounds its the grandchild,
+ // either way the parent of the textview will have the pkgName in its tag
+ final View viewWithTag = (View) tv.getParent();
+ final String pkgName = (String) viewWithTag.getTag(R.id.tag_key_package_name);
+ Long cmpntId = (Long) viewWithTag.getTag(R.id.tag_key_component_id);
+ if (cmpntId == null) {
+ cmpntId = DEFAULT_COMPONENT_ID;
+ }
+ Boolean isLLS = (Boolean) viewWithTag.getTag(R.id.tag_key_live_lock_screen);
+ highlightTitle = false;
+ if (selectedComponents != null && isLLS != null) {
+ if ((TextUtils.equals(selectedComponents.get(MODIFIES_LOCKSCREEN), pkgName)
+ && !isLLS) || (TextUtils.equals(selectedComponents.get(
+ MODIFIES_LIVE_LOCK_SCREEN), pkgName) && isLLS)) {
+ highlightTitle = true;
+ }
+ } else if (pkgName.equals(selectedPkgName) && cmpntId == selectedComponentId) {
+ highlightTitle = true;
+ }
+ if (highlightTitle) {
+ tv.setTextColor(res.getColor(R.color.component_selection_current_text_color));
+ } else {
+ tv.setTextColor(res.getColor(android.R.color.white));
+ }
+ }
+ }
+ }
+
+ public String getComponentType() {
+ return mComponentType;
+ }
+
+ public void setNumItemsPerPage(int itemsPerPage) {
+ if (mItemsPerPage != itemsPerPage) {
+ mItemsPerPage = itemsPerPage;
+ }
+ }
+
+ public void setHeight(int height) {
+ ViewGroup.LayoutParams params = mContent.getLayoutParams();
+ if (params.height != height) {
+ params.height = height;
+ mContent.setLayoutParams(params);
+ requestLayout();
+ }
+ }
+
+ public void show(String componentType, int itemsPerPage, int height) {
+ show(componentType, null, itemsPerPage, height);
+ }
+
+ public void show(String componentType, String selectedPkgName, int itemsPerPage, int height) {
+ show(componentType, selectedPkgName, DEFAULT_COMPONENT_ID, itemsPerPage, height);
+ }
+
+ public void show(String componentType, String selectedPkgName, long selectedComponentId,
+ int itemsPerPage, int height) {
+ setNumItemsPerPage(itemsPerPage);
+ setHeight(height);
+ setComponentType(componentType, selectedPkgName, selectedComponentId);
+ show();
+ }
+
+ public void show() {
+ if (getVisibility() == View.GONE) {
+ setEnabled(true);
+ setVisibility(View.VISIBLE);
+ startAnimation(mAnimateIn);
+ }
+ }
+
+ public void hide() {
+ if (getVisibility() == View.VISIBLE && isEnabled()) {
+ setEnabled(false);
+ startAnimation(mAnimateOut);
+ }
+ if (mMediaPlayer != null && mMediaPlayer.isPlaying()) mMediaPlayer.stop();
+ }
+
+ private int getLoaderIdFromComponent(String component) {
+ if (MODIFIES_STATUS_BAR.equals(component)) {
+ return LOADER_ID_STATUS_BAR;
+ }
+ if (MODIFIES_NAVIGATION_BAR.equals(component)) {
+ return LOADER_ID_NAVIGATION_BAR;
+ }
+ if (MODIFIES_FONTS.equals(component)) {
+ return LOADER_ID_FONT;
+ }
+ if (MODIFIES_ICONS.equals(component)) {
+ return LOADER_ID_ICONS;
+ }
+ if (MODIFIES_OVERLAYS.equals(component)) {
+ return LOADER_ID_STYLE;
+ }
+ if (MODIFIES_LAUNCHER.equals(component)) {
+ return LOADER_ID_WALLPAPER;
+ }
+ if (MODIFIES_BOOT_ANIM.equals(component)) {
+ return LOADER_ID_BOOT_ANIMATION;
+ }
+ if (MODIFIES_RINGTONES.equals(component)) {
+ return LOADER_ID_RINGTONE;
+ }
+ if (MODIFIES_NOTIFICATIONS.equals(component)) {
+ return LOADER_ID_NOTIFICATION;
+ }
+ if (MODIFIES_ALARMS.equals(component)) {
+ return LOADER_ID_ALARM;
+ }
+ if (MODIFIES_LOCKSCREEN.equals(component)) {
+ return LOADER_ID_LOCKSCREEN;
+ }
+ return -1;
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ return CursorLoaderHelper.componentSelectorCursorLoader(mContext, id);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, final Cursor data) {
+ int currentLoaderId = loader.getId();
+ int count = data.getCount();
+ int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
+ final Resources res = getResources();
+ int dividerPadding = res.getDimensionPixelSize(R.dimen.component_divider_padding_top);
+ int dividerHeight = res.getDimensionPixelSize(R.dimen.component_divider_height);
+ MatrixCursor lockScreenMatrixCursor = null;
+
+ switch (currentLoaderId) {
+ case LOADER_ID_ALARM:
+ case LOADER_ID_NOTIFICATION:
+ case LOADER_ID_RINGTONE:
+ mItemParams = new LayoutParams(screenWidth,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ // Sounds are a special case where there are two items laid out
+ // vertically. This layout is added as a single item so we need to
+ // adjust the count by dividing by the number of items per page and
+ // rounding up so we include all items.
+ count = (int) Math.ceil((double)count / mItemsPerPage);
+ mContent.setShowDividers(LinearLayout.SHOW_DIVIDER_NONE);
+ break;
+ case LOADER_ID_BOOT_ANIMATION:
+ dividerPadding = res.getDimensionPixelSize(
+ R.dimen.component_divider_padding_top_bootani);
+ dividerHeight = res.getDimensionPixelSize(R.dimen.component_divider_height_bootani);
+ // fall through to default
+ default:
+ mItemParams = new LayoutParams(screenWidth / mItemsPerPage,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ if (currentLoaderId == LOADER_ID_WALLPAPER ||
+ currentLoaderId == LOADER_ID_LOCKSCREEN) {
+ count += EXTRA_WALLPAPER_COMPONENTS;
+ }
+
+ if (currentLoaderId == LOADER_ID_LOCKSCREEN) {
+ lockScreenMatrixCursor = splitLockScreenCursor(data);
+ if (lockScreenMatrixCursor != null) {
+ count = lockScreenMatrixCursor.getCount() + EXTRA_WALLPAPER_COMPONENTS;
+ }
+ }
+
+ mContent.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
+ break;
+ }
+
+ mContent.setDividerPadding(dividerPadding);
+ mContent.setDividerHeight(dividerHeight);
+
+ new LoadItemsTask().execute((lockScreenMatrixCursor != null)
+ ? lockScreenMatrixCursor : data, count);
+ }
+
+ /* Some themes might contain a lock wallpaper AND a live lock screen.
+ * ThemesProvider will return one single row containing both thumbnail paths
+ * (ThemesProvider groups by theme_id) so we need to create a cursor on the
+ * fly to split that row into 2 to properly generate a view for each thumbnail
+ */
+ private MatrixCursor splitLockScreenCursor(Cursor data) {
+ int lockWallPaperThumbnailIndx, llsThumbnailIndx, pkgIndx;
+ String lockWallPaperThumbnail, liveLockScreenThumbnail, pkgName;
+ MatrixCursor lockScreenMatrixCursor;
+ int needToSplitRowAt = -1;
+
+ lockWallPaperThumbnailIndx = data.getColumnIndex(PreviewColumns.LOCK_WALLPAPER_THUMBNAIL);
+ llsThumbnailIndx = data.getColumnIndex(PreviewColumns.LIVE_LOCK_SCREEN_THUMBNAIL);
+ pkgIndx = data.getColumnIndex(ThemesColumns.PKG_NAME);
+
+ if (lockWallPaperThumbnailIndx < 0 || llsThumbnailIndx < 0 || pkgIndx < 0) {
+ //An invalid cursor was provided, we can't continue processing
+ Log.e(TAG, "Failed to process cursor due to missing columns");
+ return null;
+ }
+
+ //Let's find out if we really need to allocate a MatrixCursor.
+ //If we find at least one row with valid data in lock_wallpaper_thumbnail AND
+ //live_lock_screen_thumbnail it means we do.
+ data.moveToPosition(-1);
+ while (data.moveToNext()) {
+ lockWallPaperThumbnail = data.getString(lockWallPaperThumbnailIndx);
+ liveLockScreenThumbnail = data.getString(llsThumbnailIndx);
+ if (!TextUtils.isEmpty(lockWallPaperThumbnail)
+ && !TextUtils.isEmpty(liveLockScreenThumbnail)) {
+ needToSplitRowAt = data.getPosition();
+ break;
+ }
+ }
+
+ if (needToSplitRowAt == -1) return null;
+
+ lockScreenMatrixCursor = new MatrixCursor(data.getColumnNames());
+ //Clone all the *regular* rows up to needToSplitRowAt
+ for (int indx = 0; indx < needToSplitRowAt; indx++) {
+ data.moveToPosition(indx);
+ lockScreenMatrixCursor.addRow(CursorLoaderHelper.getRowFromCursor(data));
+ }
+ if (needToSplitRowAt == 0) {
+ data.moveToPosition(-1);
+ }
+ while (data.moveToNext()) {
+ lockWallPaperThumbnail = data.getString(lockWallPaperThumbnailIndx);
+ liveLockScreenThumbnail = data.getString(llsThumbnailIndx);
+ if (!TextUtils.isEmpty(lockWallPaperThumbnail)
+ && !TextUtils.isEmpty(liveLockScreenThumbnail)) {
+ pkgName = data.getString(pkgIndx);
+
+ MatrixCursor.RowBuilder lockWallpaperRow = lockScreenMatrixCursor.newRow();
+ MatrixCursor.RowBuilder liveLockScreenRow = lockScreenMatrixCursor.newRow();
+
+ for (String col : data.getColumnNames()) {
+ if (TextUtils.equals(col, PreviewColumns.LOCK_WALLPAPER_THUMBNAIL)) {
+ lockWallpaperRow.add(PreviewColumns.LOCK_WALLPAPER_THUMBNAIL,
+ lockWallPaperThumbnail);
+ liveLockScreenRow.add(PreviewColumns.LOCK_WALLPAPER_THUMBNAIL, null);
+ } else if (TextUtils.equals(col, PreviewColumns.LIVE_LOCK_SCREEN_THUMBNAIL)) {
+ lockWallpaperRow.add(PreviewColumns.LIVE_LOCK_SCREEN_THUMBNAIL, null);
+ liveLockScreenRow.add(PreviewColumns.LIVE_LOCK_SCREEN_THUMBNAIL,
+ liveLockScreenThumbnail);
+ } else if (TextUtils.equals(col, MODIFIES_LIVE_LOCK_SCREEN)) {
+ lockWallpaperRow.add(MODIFIES_LIVE_LOCK_SCREEN, 0);
+ liveLockScreenRow.add(MODIFIES_LIVE_LOCK_SCREEN, 1);
+ } else {
+ int colIndx = data.getColumnIndex(col);
+ lockWallpaperRow.add(col, CursorLoaderHelper.getFieldValueFromRow(data,
+ colIndx));
+ liveLockScreenRow.add(col, CursorLoaderHelper.getFieldValueFromRow(data,
+ colIndx));
+ }
+ }
+ } else {
+ //This is a regular row, so just clone it
+ lockScreenMatrixCursor.addRow(CursorLoaderHelper.getRowFromCursor(data));
+ }
+ }
+ return lockScreenMatrixCursor;
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ }
+
+ public void setOnItemClickedListener(OnItemClickedListener listener) {
+ mListener = listener;
+ }
+
+ public void setOnOpenCloseListener(OnOpenCloseListener listener) {
+ mOpenCloseListener = listener;
+ }
+
+ private View newView(Cursor cursor, int position, ViewGroup container) {
+ if (MODIFIES_STATUS_BAR.equals(mComponentType)) {
+ return newStatusBarView(cursor, container, position);
+ }
+ if (MODIFIES_NAVIGATION_BAR.equals(mComponentType)) {
+ return newNavBarView(cursor, container, position);
+ }
+ if (MODIFIES_FONTS.equals(mComponentType)) {
+ return newFontView(cursor, container, position);
+ }
+ if (MODIFIES_ICONS.equals(mComponentType)) {
+ return newIconView(cursor, container, position);
+ }
+ if (MODIFIES_OVERLAYS.equals(mComponentType)) {
+ return newStyleView(cursor, container, position);
+ }
+ if (MODIFIES_LAUNCHER.equals(mComponentType)) {
+ return newWallpapersView(cursor, container, position,
+ cursor.getColumnIndex(PreviewColumns.WALLPAPER_THUMBNAIL), false,
+ EXTRA_WALLPAPER_COMPONENTS);
+ }
+ if (MODIFIES_BOOT_ANIM.equals(mComponentType)) {
+ return newBootanimationView(cursor, container, position);
+ }
+ if (MODIFIES_RINGTONES.equals(mComponentType) ||
+ MODIFIES_NOTIFICATIONS.equals(mComponentType) ||
+ MODIFIES_ALARMS.equals(mComponentType)) {
+ return newSoundView(cursor, container, position, mComponentType);
+ }
+ if (MODIFIES_LOCKSCREEN.equals(mComponentType)) {
+ boolean isLiveLockScreen = false;
+ if (position >= EXTRA_LOCK_SCREEN_WALLPAPER_COMPONENTS) {
+ cursor.moveToPosition(position - EXTRA_LOCK_SCREEN_WALLPAPER_COMPONENTS);
+ int liveLockIndex = cursor.getColumnIndex(MODIFIES_LIVE_LOCK_SCREEN);
+ isLiveLockScreen = liveLockIndex >= 0 &&
+ cursor.getInt(liveLockIndex) == 1;
+ }
+ int index = isLiveLockScreen
+ ? cursor.getColumnIndex(PreviewColumns.LIVE_LOCK_SCREEN_THUMBNAIL)
+ : cursor.getColumnIndex(PreviewColumns.LOCK_WALLPAPER_THUMBNAIL);
+ return newWallpapersView(cursor, container, position, index, isLiveLockScreen,
+ EXTRA_LOCK_SCREEN_WALLPAPER_COMPONENTS);
+ }
+ return null;
+ }
+
+ private View newStatusBarView(Cursor cursor, ViewGroup parent, int position) {
+ cursor.moveToPosition(position);
+ View v = mInflater.inflate(R.layout.status_bar_component_selection_item,
+ parent, false);
+ int wifiIndex = cursor.getColumnIndex(PreviewColumns.STATUSBAR_WIFI_ICON);
+ int signalIndex = cursor.getColumnIndex(PreviewColumns.STATUSBAR_SIGNAL_ICON);
+ int bluetoothIndex = cursor.getColumnIndex(PreviewColumns.STATUSBAR_BLUETOOTH_ICON);
+ int batteryIndex = cursor.getColumnIndex(Utils.getBatteryIndex(mBatteryStyle));
+ int backgroundIndex = cursor.getColumnIndex(PreviewColumns.STATUSBAR_BACKGROUND);
+ int pkgNameIndex = cursor.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME);
+
+ ((ImageView) v.findViewById(R.id.slot1)).setImageBitmap(
+ Utils.loadBitmapBlob(cursor, wifiIndex));
+ ((ImageView) v.findViewById(R.id.slot2)).setImageBitmap(
+ Utils.loadBitmapBlob(cursor, signalIndex));
+ ((ImageView) v.findViewById(R.id.slot3)).setImageBitmap(
+ Utils.loadBitmapBlob(cursor, bluetoothIndex));
+ ((ImageView) v.findViewById(R.id.slot4)).setImageBitmap(
+ Utils.loadBitmapBlob(cursor, batteryIndex));
+ setTitle(((TextView) v.findViewById(R.id.title)), cursor);
+ v.findViewById(R.id.container).setBackground(
+ new BitmapDrawable(Utils.loadBitmapBlob(cursor, backgroundIndex)));
+ v.setTag(R.id.tag_key_package_name, cursor.getString(pkgNameIndex));
+ v.setOnClickListener(mItemClickListener);
+ return v;
+ }
+
+ private View newNavBarView(Cursor cursor, ViewGroup parent, int position) {
+ cursor.moveToPosition(position);
+ View v = mInflater.inflate(R.layout.navigation_bar_component_selection_item, parent,
+ false);
+ int backIndex = cursor.getColumnIndex(PreviewColumns.NAVBAR_BACK_BUTTON);
+ int backgroundIndex = cursor.getColumnIndex(PreviewColumns.STATUSBAR_BACKGROUND);
+ int pkgNameIndex = cursor.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME);
+
+ ((ImageView) v.findViewById(R.id.back)).setImageBitmap(
+ Utils.loadBitmapBlob(cursor, backIndex));
+ setTitle(((TextView) v.findViewById(R.id.title)), cursor);
+ v.findViewById(R.id.container).setBackground(
+ new BitmapDrawable(Utils.loadBitmapBlob(cursor, backgroundIndex)));
+ v.setTag(R.id.tag_key_package_name, cursor.getString(pkgNameIndex));
+ v.setOnClickListener(mItemClickListener);
+ return v;
+ }
+
+ private View newFontView(Cursor cursor, ViewGroup parent, int position) {
+ cursor.moveToPosition(position);
+ View v = mInflater.inflate(R.layout.font_component_selection_item, parent, false);
+ int pkgNameIndex = cursor.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME);
+
+ TextView preview = (TextView) v.findViewById(R.id.text_preview);
+ String pkgName = cursor.getString(pkgNameIndex);
+
+ ThemedTypefaceHelper helper = mTypefaceCache.getHelperForTheme(mContext, pkgName);
+ Typeface typefaceNormal = helper.getTypeface(Typeface.NORMAL);
+ preview.setTypeface(typefaceNormal);
+
+ setTitle(((TextView) v.findViewById(R.id.title)), cursor);
+ v.setTag(R.id.tag_key_package_name, cursor.getString(pkgNameIndex));
+ v.setOnClickListener(mItemClickListener);
+ return v;
+ }
+
+ private View newIconView(Cursor cursor, ViewGroup parent, int position) {
+ cursor.moveToPosition(position);
+ View v = mInflater.inflate(R.layout.icon_component_selection_item, parent,
+ false);
+ int iconIndex = cursor.getColumnIndex(PreviewColumns.ICON_PREVIEW_1);
+ int pkgNameIndex = cursor.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME);
+
+ ((ImageView) v.findViewById(R.id.icon)).setImageBitmap(
+ Utils.loadBitmapBlob(cursor, iconIndex));
+ setTitle(((TextView) v.findViewById(R.id.title)), cursor);
+ v.setTag(R.id.tag_key_package_name, cursor.getString(pkgNameIndex));
+ v.setOnClickListener(mItemClickListener);
+ return v;
+ }
+
+ private View newStyleView(Cursor cursor, ViewGroup parent, int position) {
+ cursor.moveToPosition(position);
+ View v = mInflater.inflate(R.layout.icon_component_selection_item, parent,
+ false);
+ int styleIndex = cursor.getColumnIndex(PreviewColumns.STYLE_THUMBNAIL);
+ int pkgNameIndex = cursor.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME);
+
+ ((ImageView) v.findViewById(R.id.icon)).setImageBitmap(
+ Utils.loadBitmapBlob(cursor, styleIndex));
+ setTitle(((TextView) v.findViewById(R.id.title)), cursor);
+ v.setTag(R.id.tag_key_package_name, cursor.getString(pkgNameIndex));
+ v.setOnClickListener(mItemClickListener);
+ return v;
+ }
+
+ private View newWallpapersView(Cursor cursor, ViewGroup parent, int position,
+ int wallpaperIndex, boolean isLiveLockScreen, int numExtraComponents) {
+ View v = mInflater.inflate(R.layout.wallpaper_component_selection_item, parent,
+ false);
+ ImageView iv = (ImageView) v.findViewById(R.id.icon);
+ if (position == 0) {
+ iv.setImageResource(R.drawable.img_wallpaper_none);
+ v.setTag(R.id.tag_key_package_name, "");
+ ((TextView) v.findViewById(R.id.title)).setText(R.string.wallpaper_none_title);
+ } else if (position == 1) {
+ iv.setImageResource(R.drawable.img_wallpaper_external);
+ v.setTag(R.id.tag_key_package_name, EXTERNAL_WALLPAPER);
+ ((TextView) v.findViewById(R.id.title))
+ .setText(R.string.wallpaper_external_title);
+ } else if (numExtraComponents == EXTRA_LOCK_SCREEN_WALLPAPER_COMPONENTS && position == 2) {
+ // TODO: update drawable once the asset is provided by design
+ iv.setImageResource(android.R.drawable.ic_lock_lock);
+ v.setTag(R.id.tag_key_package_name, MOD_LOCK);
+ ((TextView) v.findViewById(R.id.title))
+ .setText(R.string.mod_lock_title);
+ } else {
+ cursor.moveToPosition(position - numExtraComponents);
+ int pkgNameIndex = cursor.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME);
+ int cmpntIdIndex = cursor.getColumnIndex(PreviewColumns.COMPONENT_ID);
+ long cmpntId = (cmpntIdIndex >= 0) ?
+ cursor.getLong(cmpntIdIndex) : DEFAULT_COMPONENT_ID;
+ iv.setImageBitmap(
+ Utils.loadBitmapBlob(cursor, wallpaperIndex));
+ setTitle(((TextView) v.findViewById(R.id.title)), cursor);
+ v.setTag(R.id.tag_key_package_name, cursor.getString(pkgNameIndex));
+ v.setTag(R.id.tag_key_component_id, cmpntId);
+ v.setTag(R.id.tag_key_live_lock_screen, isLiveLockScreen);
+ v.findViewById(R.id.live_lock_screen_badge)
+ .setVisibility(isLiveLockScreen ? View.VISIBLE : View.GONE);
+ }
+ v.setOnClickListener(mItemClickListener);
+ return v;
+ }
+
+ private View newBootanimationView(Cursor cursor, ViewGroup parent, int position) {
+ cursor.moveToPosition(position);
+ View v = mInflater.inflate(R.layout.bootani_component_selection_item, parent,
+ false);
+ int wallpaperIndex = cursor.getColumnIndex(PreviewColumns.BOOTANIMATION_THUMBNAIL);
+ int pkgNameIndex = cursor.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME);
+
+ ((ImageView) v.findViewById(R.id.preview)).setImageBitmap(
+ Utils.loadBitmapBlob(cursor, wallpaperIndex));
+ setTitle(((TextView) v.findViewById(R.id.title)), cursor);
+ v.setTag(R.id.tag_key_package_name, cursor.getString(pkgNameIndex));
+ v.setOnClickListener(mItemClickListener);
+ return v;
+ }
+
+ private View newSoundView(Cursor cursor, ViewGroup parent, int position,
+ final String component) {
+ LinearLayout container = (LinearLayout) mInflater.inflate(
+ R.layout.component_selection_sounds_pager_item, parent, false);
+ container.setWeightSum(mItemsPerPage);
+ for (int i = 0; i < mItemsPerPage; i++) {
+ int index = position * mItemsPerPage + i;
+ if (cursor.getCount() <= index) continue;
+ cursor.moveToPosition(index);
+ View v = mInflater.inflate(R.layout.sound_component_selection_item, parent,
+ false);
+ final int pkgNameIndex = cursor.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME);
+
+ setTitle(((TextView) v.findViewById(R.id.title)), cursor);
+ v.setTag(R.id.tag_key_package_name, cursor.getString(pkgNameIndex));
+ v.setOnClickListener(mItemClickListener);
+ container.addView(v, mSoundItemParams);
+ final View playButton = v.findViewById(R.id.play_button);
+ playButton.setTag(cursor.getString(pkgNameIndex));
+ playButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int type;
+ String pkgName = (String) v.getTag();
+ if (component.equals(MODIFIES_RINGTONES)) {
+ type = RingtoneManager.TYPE_RINGTONE;
+ } else if (component.equals(MODIFIES_NOTIFICATIONS)) {
+ type = RingtoneManager.TYPE_NOTIFICATION;
+ } else {
+ type = RingtoneManager.TYPE_ALARM;
+ }
+ boolean shouldStop = playButton == mCurrentPlayPause;
+ try {
+ if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+ mMediaPlayer.stop();
+ if (mCurrentPlayPause != null) {
+ mCurrentPlayPause.setImageResource(
+ R.drawable.media_sound_selector_preview);
+ }
+ mCurrentPlayPause = null;
+ }
+ if (mCurrentPlayPause != playButton && !shouldStop) {
+ AudioUtils.loadThemeAudible(mContext, type, pkgName,
+ mMediaPlayer);
+ mMediaPlayer.start();
+ mCurrentPlayPause = (ImageView) playButton;
+ mCurrentPlayPause.setImageResource(
+ R.drawable.media_sound_selector_stop);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Unable to play preview sound", e);
+ }
+ }
+ });
+ }
+
+ return container;
+ }
+
+ private class LoadItemsTask extends AsyncTask<Object, Void, Void> {
+
+ @Override
+ protected Void doInBackground(Object... params) {
+ Cursor c = (Cursor) params[0];
+ int count = (Integer) params[1];
+ for (int i = 0; i < count && !isCancelled(); i++) {
+ final View v = newView(c, i, mContent);
+ mContent.post(new Runnable() {
+ @Override
+ public void run() {
+ mContent.addView(v, mItemParams);
+ }
+ });
+ }
+
+ if (c instanceof MatrixCursor) {
+ c.close();
+ }
+
+ // destroy the loader now that we are done with it
+ ComponentSelector.this.post(new Runnable() {
+ @Override
+ public void run() {
+ ((FragmentActivity)mContext).getSupportLoaderManager().destroyLoader(
+ getLoaderIdFromComponent(mComponentType));
+ }
+ });
+ return null;
+ }
+ }
+
+ private void setTitle(TextView titleView, Cursor cursor) {
+ String pkgName = cursor.getString(cursor.getColumnIndex(ThemesColumns.PKG_NAME));
+ int cmpntIdIndex = cursor.getColumnIndex(PreviewColumns.COMPONENT_ID);
+ long cmpntId = DEFAULT_COMPONENT_ID;
+ if (cmpntIdIndex >= 0) {
+ cmpntId = cursor.getLong(cmpntIdIndex);
+ }
+ if (Utils.getDefaultThemePackageName(mContext).equals(pkgName)) {
+ titleView.setText(mContext.getString(R.string.default_tag_text));
+ titleView.setTypeface(titleView.getTypeface(), Typeface.BOLD);
+ } else {
+ titleView.setText(cursor.getString(cursor.getColumnIndex(ThemesColumns.TITLE)));
+ }
+ boolean highlightTitle = false;
+ if (mComponentType.equals(MODIFIES_LOCKSCREEN)) {
+ Map<String, String> selectedComponents = ((ChooserActivity)mContext)
+ .getSelectedComponentsMap();
+ int isLLS = cursor.getInt(cursor.getColumnIndex(MODIFIES_LIVE_LOCK_SCREEN));
+ if ((TextUtils.equals(selectedComponents.get(MODIFIES_LOCKSCREEN), pkgName)
+ && isLLS == 0) || (TextUtils.equals(
+ selectedComponents.get(MODIFIES_LIVE_LOCK_SCREEN), pkgName) && isLLS == 1)) {
+ highlightTitle = true;
+ }
+ } else if (pkgName.equals(mSelectedComponentPkgName) && cmpntId == mSelectedComponentId) {
+ highlightTitle = true;
+ }
+ if (highlightTitle) {
+ titleView.setTextColor(getResources().getColor(
+ R.color.component_selection_current_text_color));
+ }
+ }
+
+ private OnClickListener mItemClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ long cmpntId = DEFAULT_COMPONENT_ID;
+ String pkgName = (String) v.getTag(R.id.tag_key_package_name);
+ Long cmpntIdTag = (Long) v.getTag(R.id.tag_key_component_id);
+ if (cmpntIdTag != null) {
+ cmpntId = cmpntIdTag;
+ }
+ Boolean isLiveLock = (Boolean) v.getTag(R.id.tag_key_live_lock_screen);
+ boolean isSamePkgButDifferentLockScreen = false;
+ Bundle params = null;
+ if (isLiveLock != null) {
+ params = new Bundle();
+ params.putBoolean(IS_LIVE_LOCK_SCREEN_VIEW, isLiveLock);
+
+ if (pkgName.equals(mSelectedComponentPkgName) && v != mPrevLockScreenView) {
+ isSamePkgButDifferentLockScreen = true;
+ }
+ mPrevLockScreenView = v;
+ }
+ if (DEBUG_SELECTOR) Toast.makeText(mContext, pkgName, Toast.LENGTH_SHORT).show();
+ if (mListener != null && (isSamePkgButDifferentLockScreen ||
+ !pkgName.equals(mSelectedComponentPkgName) ||
+ pkgName.equals(EXTERNAL_WALLPAPER) || cmpntId != mSelectedComponentId)) {
+ mSelectedComponentPkgName = pkgName;
+ mSelectedComponentId = cmpntId;
+ mListener.onItemClicked(pkgName, cmpntId, params);
+ final int count = mContent.getChildCount();
+ final Resources res = getResources();
+ for (int i = 0; i < count; i++) {
+ final View child = mContent.getChildAt(i);
+ final TextView tv = (TextView) child.findViewById(R.id.title);
+ if (tv != null) {
+ if (child == v) {
+ tv.setTextColor(
+ res.getColor(R.color.component_selection_current_text_color));
+ } else {
+ tv.setTextColor(res.getColor(android.R.color.white));
+ }
+ }
+ }
+ }
+ }
+ };
+
+ private class ThemesObserver extends ContentObserver {
+ public ThemesObserver() {
+ super(null);
+ }
+
+ public void register() {
+ mContext.getContentResolver().registerContentObserver(
+ ThemesColumns.CONTENT_URI, false, this);
+ }
+
+ public void unregister() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ // reload items by calling setComponentType()
+ if (mComponentType != null) {
+ final String componentType = mComponentType;
+ mComponentType = null;
+ mContent.post(new Runnable() {
+ @Override
+ public void run() {
+ setComponentType(componentType, mSelectedComponentPkgName);
+ }
+ });
+ }
+ }
+ }
+
+ public interface OnItemClickedListener {
+ public void onItemClicked(String pkgName, long componentId, Bundle params);
+ }
+
+ public interface OnOpenCloseListener {
+ public void onSelectorOpened();
+ public void onSelectorClosed();
+ public void onSelectorClosing();
+ }
+} \ No newline at end of file
diff --git a/src/org/cyanogenmod/theme/chooser2/IconTransitionDrawable.java b/src/org/cyanogenmod/theme/chooser2/IconTransitionDrawable.java
new file mode 100644
index 0000000..08cc910
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/IconTransitionDrawable.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.SystemClock;
+
+/**
+ * An extension of LayerDrawables that is intended to cross-fade between
+ * the first and second layer. To start the transition, call {@link #startTransition(int)}. To
+ * display just the first layer, call {@link #resetTransition()}.
+ * <p>
+ * It can be defined in an XML file with the <code>&lt;transition></code> element.
+ * Each Drawable in the transition is defined in a nested <code>&lt;item></code>. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
+ *
+ * @attr ref android.R.styleable#LayerDrawableItem_left
+ * @attr ref android.R.styleable#LayerDrawableItem_top
+ * @attr ref android.R.styleable#LayerDrawableItem_right
+ * @attr ref android.R.styleable#LayerDrawableItem_bottom
+ * @attr ref android.R.styleable#LayerDrawableItem_drawable
+ * @attr ref android.R.styleable#LayerDrawableItem_id
+ *
+ */
+public class IconTransitionDrawable extends LayerDrawable {
+
+ /**
+ * A transition is about to start.
+ */
+ private static final int TRANSITION_STARTING = 0;
+
+ /**
+ * The transition has started and the animation is in progress
+ */
+ private static final int TRANSITION_RUNNING = 1;
+
+ /**
+ * No transition will be applied
+ */
+ private static final int TRANSITION_NONE = 2;
+
+ /**
+ * The current state of the transition. One of {@link #TRANSITION_STARTING},
+ * {@link #TRANSITION_RUNNING} and {@link #TRANSITION_NONE}
+ */
+ private int mTransitionState = TRANSITION_NONE;
+
+ private long mStartTimeMillis;
+ private int mFrom;
+ private int mTo;
+ private int mDuration;
+ private int mAlpha = 0;
+ private float mFromScale;
+ private float mToScale;
+
+ /**
+ * Create a new transition drawable with the specified list of layers. At least
+ * 2 layers are required for this drawable to work properly.
+ */
+ public IconTransitionDrawable(Drawable[] layers) {
+ super(layers);
+ }
+
+ /**
+ * Begin the second layer on top of the first layer.
+ *
+ * @param durationMillis The length of the transition in milliseconds
+ */
+ public void startTransition(int durationMillis) {
+ mFrom = 0;
+ mTo = 255;
+ mAlpha = 0;
+ mFromScale = 0f;
+ mToScale = 1.0f;
+ mDuration = durationMillis;
+ mTransitionState = TRANSITION_STARTING;
+ invalidateSelf();
+ }
+
+ /**
+ * Show only the first layer.
+ */
+ public void resetTransition() {
+ mAlpha = 0;
+ mTransitionState = TRANSITION_NONE;
+ invalidateSelf();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ boolean done = true;
+ float scale = 0f;
+
+ switch (mTransitionState) {
+ case TRANSITION_STARTING:
+ mStartTimeMillis = SystemClock.uptimeMillis();
+ done = false;
+ mTransitionState = TRANSITION_RUNNING;
+ break;
+
+ case TRANSITION_RUNNING:
+ if (mStartTimeMillis >= 0) {
+ float normalized = (float)
+ (SystemClock.uptimeMillis() - mStartTimeMillis) / mDuration;
+ done = normalized >= 1.0f;
+ normalized = Math.min(normalized, 1.0f);
+ mAlpha = (int) (mFrom + (mTo - mFrom) * normalized);
+ scale = mFromScale + (mToScale - mFromScale) * normalized;
+ }
+ break;
+ }
+
+ final int alpha = mAlpha;
+
+ if (done) {
+ // the setAlpha() calls below trigger invalidation and redraw. If we're done, just draw
+ // the appropriate drawable[s] and return
+ if (alpha == 0) {
+ getDrawable(0).draw(canvas);
+ }
+ if (alpha == 0xFF) {
+ getDrawable(1).draw(canvas);
+
+ }
+ return;
+ }
+
+ Drawable d;
+ d = getDrawable(0);
+ d.setAlpha(255 - alpha);
+ int cx = getIntrinsicWidth() / 2;
+ int cy = getIntrinsicHeight() / 2;
+ canvas.save();
+ canvas.scale(1.0f - scale, 1.0f - scale, cx, cy);
+ d.draw(canvas);
+ canvas.restore();
+ d.setAlpha(0xFF);
+
+ if (alpha > 0) {
+ d = getDrawable(1);
+ d.setAlpha(alpha);
+ canvas.save();
+ canvas.scale(scale, scale, cx, cy);
+ d.draw(canvas);
+ canvas.restore();
+ d.setAlpha(0xFF);
+ }
+
+ if (!done) {
+ invalidateSelf();
+ }
+ }
+}
diff --git a/src/org/cyanogenmod/theme/chooser2/MyThemeFragment.java b/src/org/cyanogenmod/theme/chooser2/MyThemeFragment.java
new file mode 100644
index 0000000..490b568
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/MyThemeFragment.java
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.MediaPlayer;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.content.Loader;
+import android.util.Log;
+import android.util.MutableLong;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.cyanogenmod.theme.util.AudioUtils;
+import org.cyanogenmod.theme.util.CursorLoaderHelper;
+import org.cyanogenmod.theme.util.PreferenceUtils;
+import org.cyanogenmod.theme.util.ThemedTypefaceHelper;
+import org.cyanogenmod.theme.util.TypefaceHelperCache;
+import org.cyanogenmod.theme.util.Utils;
+
+import cyanogenmod.providers.ThemesContract;
+import cyanogenmod.providers.ThemesContract.PreviewColumns;
+import cyanogenmod.providers.ThemesContract.ThemesColumns;
+import cyanogenmod.themes.ThemeChangeRequest;
+import cyanogenmod.themes.ThemeChangeRequest.RequestType;
+import cyanogenmod.themes.ThemeManager;
+
+import org.cyanogenmod.internal.util.ThemeUtils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_ALL;
+
+public class MyThemeFragment extends ThemeFragment {
+ private static final String TAG = MyThemeFragment.class.getSimpleName();
+
+ private static final String ARG_BASE_THEME_PACKAGE_NAME = "baseThemePkgName";
+ private static final String ARG_BASE_THEME_NAME = "baseThemeName";
+ private static final String ARG_BASE_THEME_AUTHOR = "baseThemeAuthor";
+
+ private String mBaseThemeName;
+ private String mBaseThemeAuthor;
+
+ private SurfaceView mSurfaceView;
+
+ static MyThemeFragment newInstance(String baseThemePkgName, String baseThemeName,
+ String baseThemeAuthor, boolean skipLoadingAnim,
+ boolean animateToLockScreenCard) {
+ MyThemeFragment f = new MyThemeFragment();
+ Bundle args = new Bundle();
+ args.putString(ARG_PACKAGE_NAME, CURRENTLY_APPLIED_THEME);
+ args.putString(ARG_BASE_THEME_PACKAGE_NAME, baseThemePkgName);
+ args.putString(ARG_BASE_THEME_NAME, baseThemeName);
+ args.putString(ARG_BASE_THEME_AUTHOR, baseThemeAuthor);
+ args.putBoolean(ARG_SKIP_LOADING_ANIM, skipLoadingAnim);
+ args.putBoolean(ARG_ANIMATE_TO_LOCK_SCREEN_CARD, animateToLockScreenCard);
+ f.setArguments(args);
+ return f;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Context context = getActivity();
+ ThemedTypefaceHelper helper = sTypefaceHelperCache.getHelperForTheme(context,
+ getAppliedFontPackageName());
+ mTypefaceNormal = helper.getTypeface(Typeface.NORMAL);
+ mBaseThemePkgName = getArguments().getString(ARG_BASE_THEME_PACKAGE_NAME);
+ mBaseThemeName = getArguments().getString(ARG_BASE_THEME_NAME);
+ mBaseThemeAuthor = getArguments().getString(ARG_BASE_THEME_AUTHOR);
+ mShowLockScreenSelectorAfterContentLoaded = getArguments().getBoolean(
+ ARG_ANIMATE_TO_LOCK_SCREEN_CARD);
+ mSurfaceView = createSurfaceView();
+ populateBaseThemeSupportedComponents(mBaseThemePkgName);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = super.onCreateView(inflater, container, savedInstanceState);
+ mThemeTagLayout.setAppliedTagEnabled(true);
+ if (mBaseThemePkgName.equals(Utils.getDefaultThemePackageName(getActivity()))) {
+ mThemeTagLayout.setDefaultTagEnabled(true);
+ }
+ if (PreferenceUtils.hasThemeBeenUpdated(getActivity(), mBaseThemePkgName)) {
+ mThemeTagLayout.setUpdatedTagEnabled(true);
+ }
+ mDelete.setVisibility(View.GONE);
+ setCustomized(isThemeCustomized());
+ return v;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (!mExpanded && 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
+ public void setUserVisibleHint(boolean isVisibleToUser) {
+ super.setUserVisibleHint(isVisibleToUser);
+ if (mThemeTagLayout == null) return;
+
+ if (!isVisibleToUser) {
+ if (PreferenceUtils.hasThemeBeenUpdated(getActivity(), mBaseThemePkgName)) {
+ mThemeTagLayout.setUpdatedTagEnabled(true);
+ }
+ } else {
+ if (PreferenceUtils.hasThemeBeenUpdated(getActivity(), mBaseThemePkgName)) {
+ PreferenceUtils.removeUpdatedTheme(getActivity(), mBaseThemePkgName);
+ }
+ }
+ }
+
+ @Override
+ protected boolean onPopupMenuItemClick(MenuItem item) {
+ switch(item.getItemId()) {
+ case R.id.menu_reset:
+ resetTheme();
+ return true;
+ }
+
+ return super.onPopupMenuItemClick(item);
+ }
+
+ @Override
+ public void collapse(boolean applyTheme) {
+ super.collapse(applyTheme);
+ if (mSurfaceView != null) mSurfaceView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void expand() {
+ super.expand();
+ if (mSurfaceView != null && mShadowFrame.indexOfChild(mSurfaceView) >= 0) {
+ mSurfaceView.setVisibility(View.GONE);
+ mWallpaper.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ @Override
+ public void performClick(boolean clickedOnContent) {
+ if (clickedOnContent) {
+ showCustomizeResetLayout();
+ } else {
+ if (isShowingCustomizeResetLayout()) {
+ hideCustomizeResetLayout();
+ } else {
+ super.performClick(clickedOnContent);
+ }
+ }
+ }
+
+ @Override
+ public void setCurrentTheme(Map<String, String> currentTheme,
+ MutableLong currentWallpaperComponentId) {
+ super.setCurrentTheme(currentTheme, currentWallpaperComponentId);
+ for (String key : currentTheme.keySet()) {
+ mSelectedComponentsMap.put(key, currentTheme.get(key));
+ }
+ mSelectedWallpaperComponentId = currentWallpaperComponentId.value;
+ }
+
+ @Override
+ 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()) {
+ String current = mCurrentTheme.get(key);
+ if (current == null || !current.equals(mSelectedComponentsMap.get(key))) {
+ return true;
+ }
+ if (ThemesColumns.MODIFIES_LAUNCHER.equals(key) &&
+ mCurrentWallpaperComponentId.value != mSelectedWallpaperComponentId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void applyThemeWhenPopulated(String pkgName, List<String> components) {
+ super.applyThemeWhenPopulated(pkgName, components);
+ populateComponentsToApply(pkgName, components);
+ }
+
+ private void populateComponentsToApply(String pkgName, List<String> components) {
+ String selection = ThemesColumns.PKG_NAME + "=?";
+ String[] selectionArgs = { pkgName };
+ Cursor c = getActivity().getContentResolver().query(ThemesColumns.CONTENT_URI,
+ null, selection, selectionArgs, null);
+ if (c != null) {
+ if (c.getCount() > 0 && c.moveToFirst()) {
+ mSelectedComponentsMap.clear();
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_ALARMS)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_ALARMS, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_BOOT_ANIM)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_BOOT_ANIM, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_FONTS)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_FONTS, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_ICONS)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_ICONS, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_LAUNCHER)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_LAUNCHER, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_LOCKSCREEN)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_LOCKSCREEN, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_NAVIGATION_BAR)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_NAVIGATION_BAR, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_NOTIFICATIONS)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_NOTIFICATIONS, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_OVERLAYS)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_OVERLAYS, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_RINGTONES)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_RINGTONES, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_STATUS_BAR)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_STATUS_BAR, pkgName);
+ }
+ if (c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN)) == 1) {
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN, pkgName);
+ }
+ }
+ c.close();
+ }
+
+ // strip out any components that are not in the components list
+ if (components != null) {
+ Iterator<Map.Entry<String, String>> iterator =
+ mSelectedComponentsMap.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<String, String> entry = iterator.next();
+ if (!components.contains(entry.getKey())) {
+ iterator.remove();
+ }
+ }
+ }
+ }
+
+ private void loadComponentsToApply() {
+ for (String component : mSelectedComponentsMap.keySet()) {
+ loadComponentFromPackage(mSelectedComponentsMap.get(component), component,
+ mSelectedWallpaperComponentId);
+ }
+ }
+
+ private BroadcastReceiver mWallpaperChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // only update if we are the current visible fragment or if there is no theme
+ // being applied.
+ ThemeManager tm = getThemeManager();
+ if (!tm.isThemeApplying() || getUserVisibleHint()) {
+ 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 setCustomized(boolean customized) {
+ mReset.setVisibility(customized ? View.VISIBLE : View.GONE);
+ mThemeTagLayout.setCustomizedTagEnabled(customized);
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_ID_ALL:
+ if (args != null) {
+ String pkgName = args.getString(ARG_PACKAGE_NAME);
+ if (pkgName != null) {
+ return super.onCreateLoader(id, args);
+ }
+ }
+ return CursorLoaderHelper.myThemeFragmentCursorLoader(getActivity(), id);
+ default:
+ // Only LOADER_ID_ALL differs for MyThemeFragment
+ return super.onCreateLoader(id, args);
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
+ super.onLoadFinished(loader, c);
+ // if the theme is resetting, we need to apply these changes now that the supported
+ // theme components have been properly set.
+ if (loader.getId() == LOADER_ID_ALL) {
+ if (mThemeResetting) {
+ applyTheme();
+ } else if (mApplyThemeOnPopulated) {
+ loadComponentsToApply();
+ applyTheme();
+ } else if (mSelectedComponentsMap.size() == 0) {
+ //Re-populates selected components with current theme. Why?
+ //We got here because the cursor was reloaded after the user pressed back and no
+ //changes were applied, causing the selected components map to be wiped out
+ mSelectedComponentsMap.putAll(mCurrentTheme);
+ }
+ }
+ }
+
+ @Override
+ protected Map<String, String> fillMissingComponentsWithDefault(
+ Map<String, String> originalMap) {
+ // Only the ThemeFragment should be altering this, for the MyThemeFragment this is not
+ // desirable as it changes components the user did not even touch.
+ return originalMap;
+ }
+
+ @Override
+ protected ThemeChangeRequest getThemeChangeRequestForComponents(
+ Map<String, String> componentMap) {
+ return getThemeChangeRequestForComponents(componentMap, RequestType.USER_REQUEST_MIXNMATCH);
+ }
+
+ @Override
+ protected Map<String, String> getComponentsToApply() {
+ Map<String, String> componentsToApply = mThemeResetting
+ ? getEmptyComponentsMap()
+ : new HashMap<String, String>();
+ if (mThemeResetting) {
+ final String pkgName = getThemePackageName();
+ for (String component : mBaseThemeSupportedComponents) {
+ componentsToApply.put(component, pkgName);
+ }
+ } else {
+ // Only apply components that actually changed
+ for (String component : mSelectedComponentsMap.keySet()) {
+ String currentPkg = mCurrentTheme.get(component);
+ String selectedPkg = mSelectedComponentsMap.get(component);
+ if (currentPkg == null || mThemeResetting || !currentPkg.equals(selectedPkg) ||
+ mCurrentWallpaperComponentId.value != mSelectedWallpaperComponentId) {
+ componentsToApply.put(component, selectedPkg);
+ }
+ }
+ if (mExternalLockscreenUri != null) {
+ if (mCurrentTheme.containsKey(ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN)) {
+ componentsToApply.put(ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN, LOCKSCREEN_NONE);
+ }
+ if (mCurrentTheme.containsKey(ThemesColumns.MODIFIES_LOCKSCREEN)) {
+ componentsToApply.put(ThemesColumns.MODIFIES_LOCKSCREEN, LOCKSCREEN_NONE);
+ }
+ }
+ }
+ return componentsToApply;
+ }
+
+ @Override
+ protected void populateSupportedComponents(Cursor c) {
+ }
+
+ @Override
+ protected Boolean shouldShowComponentCard(String component) {
+ return true;
+ }
+
+ @Override
+ protected void loadTitle(Cursor c) {
+ mTitle.setText(mBaseThemeName);
+ mAuthor.setText(mBaseThemeAuthor);
+ }
+
+ @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);
+ return;
+ }
+ Drawable overlay = null;
+ if (animate) {
+ overlay = getOverlayDrawable(mWallpaperCard, true);
+ }
+
+ int wpIdx = c.getColumnIndex(PreviewColumns.WALLPAPER_PREVIEW);
+ final Resources res = getResources();
+ final Context context = getActivity();
+ 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) {
+ Bitmap bmp = Utils.loadBitmapBlob(c, wpIdx);
+ if (bmp != null) wp = new BitmapDrawable(res, bmp);
+ }
+ if (wp != null) {
+ mWallpaper.setImageDrawable(wp);
+ mWallpaperCard.setWallpaper(wp);
+ setCardTitle(mWallpaperCard, mCurrentTheme.get(ThemesColumns.MODIFIES_LAUNCHER),
+ getString(R.string.wallpaper_label));
+ } else {
+ mWallpaperCard.clearWallpaper();
+ mWallpaperCard.setEmptyViewEnabled(true);
+ setAddComponentTitle(mWallpaperCard, getString(R.string.wallpaper_label));
+ }
+
+ if (animate) {
+ animateContentChange(R.id.wallpaper_card, mWallpaperCard, overlay);
+ }
+ }
+
+ @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);
+ return;
+ }
+ Drawable overlay = null;
+ if (animate) {
+ overlay = getOverlayDrawable(mLockScreenCard, true);
+ }
+
+ //If the current theme includes a lock wallpaper, the WallpaperMgr will
+ //return a valid Drawable we can display in the card. However, if the user
+ //picked a LLS, we need to get the path from the provider and manually load the bitmap
+ int wpIdx = c.getColumnIndex(PreviewColumns.LIVE_LOCK_SCREEN_PREVIEW);
+ Drawable wp = null;
+ if (wpIdx >= 0) {
+ final Resources res = getResources();
+ Bitmap bmp = Utils.loadBitmapBlob(c, wpIdx);
+ if (bmp != null) wp = new BitmapDrawable(res, bmp);
+ } else {
+ final Context context = getActivity();
+ wp = context == null ? null :
+ WallpaperManager.getInstance(context).getFastKeyguardDrawable();
+ }
+ if (wp != null) {
+ mLockScreenCard.setWallpaper(wp);
+ } else if (!mSelectedComponentsMap.containsKey(ThemesColumns.MODIFIES_LOCKSCREEN)) {
+ mLockScreenCard.clearWallpaper();
+ mLockScreenCard.setEmptyViewEnabled(true);
+ setAddComponentTitle(mLockScreenCard, getString(R.string.lockscreen_label));
+ }
+
+ if (animate) {
+ animateContentChange(R.id.lockscreen_card, mLockScreenCard, overlay);
+ }
+ }
+
+ @Override
+ protected void loadFont(Cursor c, boolean animate) {
+ int pkgNameIdx = c.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME);
+ if (pkgNameIdx > -1) {
+ super.loadFont(c, animate);
+ return;
+ }
+ Drawable overlay = null;
+ if (animate) {
+ overlay = getOverlayDrawable(mFontPreview, true);
+ }
+ setCardTitle(mFontCard, mCurrentTheme.get(ThemesColumns.MODIFIES_FONTS),
+ getString(R.string.font_label));
+
+ TypefaceHelperCache cache = TypefaceHelperCache.getInstance();
+ ThemedTypefaceHelper helper = cache.getHelperForTheme(getActivity(),
+ getAppliedFontPackageName());
+ mTypefaceNormal = helper.getTypeface(Typeface.NORMAL);
+ mFontPreview.setTypeface(mTypefaceNormal);
+ if (animate) {
+ animateContentChange(R.id.font_preview_container, mFontPreview, overlay);
+ }
+ }
+
+ @Override
+ protected void loadAudible(int type, Cursor c, boolean animate) {
+ int pkgNameIdx = c.getColumnIndex(ThemesContract.ThemesColumns.PKG_NAME);
+ if (pkgNameIdx > -1) {
+ super.loadAudible(type, c, animate);
+ return;
+ }
+ ComponentCardView audibleContainer = null;
+ ImageView playPause = null;
+ String modsComponent = "";
+ switch (type) {
+ case RingtoneManager.TYPE_RINGTONE:
+ audibleContainer = mRingtoneCard;
+ playPause = mRingtonePlayPause;
+ modsComponent = ThemesColumns.MODIFIES_RINGTONES;
+ break;
+ case RingtoneManager.TYPE_NOTIFICATION:
+ audibleContainer = mNotificationCard;
+ playPause = mNotificationPlayPause;
+ modsComponent = ThemesColumns.MODIFIES_NOTIFICATIONS;
+ break;
+ case RingtoneManager.TYPE_ALARM:
+ audibleContainer = mAlarmCard;
+ playPause = mAlarmPlayPause;
+ modsComponent = ThemesColumns.MODIFIES_ALARMS;
+ break;
+ }
+ if (audibleContainer == null) return;
+
+ if (playPause == null) {
+ playPause =
+ (ImageView) audibleContainer.findViewById(R.id.play_pause);
+ }
+ TextView title = (TextView) audibleContainer.findViewById(R.id.audible_name);
+ MediaPlayer mp = mMediaPlayers.get(playPause);
+ if (mp == null) {
+ mp = new MediaPlayer();
+ }
+
+ final Context context = getActivity();
+ Ringtone ringtone = null;
+ try {
+ Uri ringtoneUri = AudioUtils.loadDefaultAudible(context, type, mp);
+ if (ringtoneUri != null) ringtone = RingtoneManager.getRingtone(context, ringtoneUri);
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to load default sound ", e);
+ }
+
+ if (ringtone != null) {
+ title.setText(ringtone.getTitle(context));
+ setCardTitle(audibleContainer, mCurrentTheme.get(modsComponent),
+ getAudibleLabel(type));
+ } else {
+ title.setText(getString(R.string.audible_title_none));
+ setAddComponentTitle(audibleContainer, getAudibleLabel(type));
+ playPause.setVisibility(View.INVISIBLE);
+ audibleContainer.setEmptyViewEnabled(true);
+ }
+
+ playPause.setTag(mp);
+ mMediaPlayers.put(playPause, mp);
+ playPause.setOnClickListener(mPlayPauseClickListener);
+ mp.setOnCompletionListener(mPlayCompletionListener);
+ }
+
+ @Override
+ protected void loadStatusBar(Cursor c, boolean animate) {
+ super.loadStatusBar(c, animate);
+ setCardTitle(mStatusBarCard, mCurrentTheme.get(ThemesColumns.MODIFIES_STATUS_BAR),
+ getString(R.string.statusbar_label));
+ }
+
+ @Override
+ protected void loadIcons(Cursor c, boolean animate) {
+ super.loadIcons(c, animate);
+ setCardTitle(mIconCard, mCurrentTheme.get(ThemesColumns.MODIFIES_ICONS),
+ getString(R.string.icon_label));
+ }
+
+ @Override
+ protected void loadNavBar(Cursor c, boolean animate) {
+ super.loadNavBar(c, animate);
+ setCardTitle(mNavBarCard, mCurrentTheme.get(ThemesColumns.MODIFIES_NAVIGATION_BAR),
+ getString(R.string.navbar_label));
+ }
+
+ @Override
+ protected void loadStyle(Cursor c, boolean animate) {
+ super.loadStyle(c, animate);
+ setCardTitle(mStyleCard, mCurrentTheme.get(ThemesColumns.MODIFIES_OVERLAYS),
+ getString(R.string.style_label));
+ }
+
+ @Override
+ protected void loadBootAnimation(Cursor c) {
+ super.loadBootAnimation(c);
+ setCardTitle(mBootAnimationCard, mCurrentTheme.get(ThemesColumns.MODIFIES_BOOT_ANIM),
+ getString(R.string.boot_animation_label));
+ }
+
+ @Override
+ public String getThemePackageName() {
+ if (mBaseThemePkgName == null) {
+ // check if the package name is defined in the arguments bundle
+ Bundle bundle = getArguments();
+ if (bundle != null) {
+ mBaseThemePkgName = bundle.getString(ARG_BASE_THEME_PACKAGE_NAME);
+ }
+ }
+ return mBaseThemePkgName;
+ }
+
+ private SurfaceView createSurfaceView() {
+ final Context context = getActivity();
+ if (context == null) return null;
+
+ SurfaceView sv = new SurfaceView(context);
+ final Resources res = context.getResources();
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+ res.getDimensionPixelSize(R.dimen.wallpaper_preview_width),
+ res.getDimensionPixelSize(R.dimen.theme_preview_height),
+ Gravity.CENTER_HORIZONTAL);
+ sv.setLayoutParams(params);
+
+ return sv;
+ }
+
+ private void addSurfaceView(SurfaceView sv) {
+ if (mShadowFrame.indexOfChild(mSurfaceView) < 0) {
+ int idx = mShadowFrame.indexOfChild(mWallpaper);
+ mShadowFrame.addView(sv, idx + 1);
+ }
+ }
+
+ private void removeSurfaceView(SurfaceView sv) {
+ if (mShadowFrame.indexOfChild(mSurfaceView) >= 0) {
+ mShadowFrame.removeView(sv);
+ }
+ }
+
+ /**
+ * Populates mBaseThemeSupportedComponents.
+ * @param pkgName Package name of the base theme used
+ */
+ private void populateBaseThemeSupportedComponents(String pkgName) {
+ String selection = ThemesColumns.PKG_NAME + "=?";
+ String[] selectionArgs = { pkgName };
+ Cursor c = getActivity().getContentResolver().query(ThemesColumns.CONTENT_URI,
+ null, selection, selectionArgs, null);
+ if (c != null) {
+ if (c.moveToFirst()) {
+ List<String> components = ThemeUtils.getAllComponents();
+ final String baseThemePackageName = getThemePackageName();
+ for (String component : components) {
+ int pkgIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+ int modifiesCompIdx = c.getColumnIndex(component);
+
+ String pkg = pkgIdx >= 0 ? c.getString(pkgIdx) : null;
+ boolean supported = (modifiesCompIdx >= 0) && (c.getInt(modifiesCompIdx) == 1);
+ if (supported && baseThemePackageName.equals(pkg)) {
+ mBaseThemeSupportedComponents.add(component);
+ }
+ }
+ }
+ c.close();
+ }
+ }
+}
diff --git a/src/org/cyanogenmod/theme/chooser2/NewFragmentStatePagerAdapter.java b/src/org/cyanogenmod/theme/chooser2/NewFragmentStatePagerAdapter.java
new file mode 100644
index 0000000..e108a76
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/NewFragmentStatePagerAdapter.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.view.PagerAdapter;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Implementation of {@link android.support.v4.view.PagerAdapter} that
+ * uses a {@link android.support.v4.app.Fragment} to manage each page. This class also handles
+ * saving and restoring of fragment's state.
+ *
+ * <p>This version of the pager is more useful when there are a large number
+ * of pages, working more like a list view. When pages are not visible to
+ * the user, their entire fragment may be destroyed, only keeping the saved
+ * state of that fragment. This allows the pager to hold on to much less
+ * memory associated with each visited page as compared to
+ * {@link android.support.v4.app.FragmentPagerAdapter} at the cost of potentially more overhead when
+ * switching between pages.
+ *
+ * <p>When using FragmentPagerAdapter the host ViewPager must have a
+ * valid ID set.</p>
+ *
+ * <p>Subclasses only need to implement {@link #getItem(int)}
+ * and {@link #getCount()} to have a working adapter.
+ *
+ * <p>Here is an example implementation of a pager containing fragments of
+ * lists:
+ *
+ * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentStatePagerSupport.java
+ * complete}
+ *
+ * <p>The <code>R.layout.fragment_pager</code> resource of the top-level fragment is:
+ *
+ * {@sample development/samples/Support13Demos/res/layout/fragment_pager.xml
+ * complete}
+ *
+ * <p>The <code>R.layout.fragment_pager_list</code> resource containing each
+ * individual fragment's layout is:
+ *
+ * {@sample development/samples/Support13Demos/res/layout/fragment_pager_list.xml
+ * complete}
+ */
+public abstract class NewFragmentStatePagerAdapter extends PagerAdapter {
+ private static final String TAG = NewFragmentStatePagerAdapter.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private final FragmentManager mFragmentManager;
+ private FragmentTransaction mCurTransaction = null;
+
+ private long[] mItemIds = new long[] {};
+ private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
+ private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
+ private Fragment mCurrentPrimaryItem = null;
+
+ public NewFragmentStatePagerAdapter(FragmentManager fm) {
+
+ mFragmentManager = fm;
+ createIdCache();
+ }
+
+ /**
+ * Return the Fragment associated with a specified position.
+ */
+ public abstract Fragment getItem(int position);
+
+ /**
+ * Return a unique identifier for the item at the given position.
+ */
+ public abstract long getItemId(int position);
+
+ @Override
+ public void startUpdate(ViewGroup container) {
+ }
+
+ private void checkForIdChanges() {
+ long[] newItemIds = new long[getCount()];
+ for (int i = 0; i < newItemIds.length; i++) {
+ newItemIds[i] = getItemId(i);
+ }
+
+ if (!Arrays.equals(mItemIds, newItemIds)) {
+ ArrayList<Fragment.SavedState> newSavedState = new ArrayList<Fragment.SavedState>();
+ ArrayList<Fragment> newFragments = new ArrayList<Fragment>();
+
+ for (int oldPosition = 0; oldPosition < mItemIds.length; oldPosition++) {
+ int newPosition = POSITION_NONE;
+ for (int i = 0; i < newItemIds.length; i++) {
+ if (mItemIds[oldPosition] == newItemIds[i]) {
+ newPosition = i;
+ break;
+ }
+ }
+ if (newPosition >= 0) {
+ if (oldPosition < mSavedState.size()) {
+ Fragment.SavedState savedState = mSavedState.get(oldPosition);
+ if (savedState != null) {
+ while (newSavedState.size() <= newPosition) {
+ newSavedState.add(null);
+ }
+ newSavedState.set(newPosition, savedState);
+ }
+ }
+ if (oldPosition < mFragments.size()) {
+ Fragment fragment = mFragments.get(oldPosition);
+ if (fragment != null) {
+ while (newFragments.size() <= newPosition) {
+ newFragments.add(null);
+ }
+ newFragments.set(newPosition, fragment);
+ }
+ }
+ }
+ }
+
+ mItemIds = newItemIds;
+ mSavedState = newSavedState;
+ mFragments = newFragments;
+ }
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ checkForIdChanges();
+
+ super.notifyDataSetChanged();
+ }
+
+ /**
+ * Create the initial set of item IDs. Run this after you have set your adapter data.
+ */
+ public void createIdCache() {
+ // If we have already stored ids, don't overwrite them
+ if (mItemIds.length == 0) {
+ // getCount might have overhead, so run it as late as possible
+ final int count = getCount();
+ if (count > 0) {
+ mItemIds = new long[count];
+ for (int i = 0; i < count; i++) {
+ mItemIds[i] = getItemId(i);
+ }
+ }
+ }
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+
+ createIdCache();
+
+ // If we already have this item instantiated, there is nothing
+ // to do. This can happen when we are restoring the entire pager
+ // from its saved state, where the fragment manager has already
+ // taken care of restoring the fragments we previously had instantiated.
+ if (mFragments.size() > position) {
+ Fragment f = mFragments.get(position);
+ if (f != null) {
+ return f;
+ }
+ }
+
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+
+ Fragment fragment = getItem(position);
+ if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
+ if (mSavedState.size() > position) {
+ Fragment.SavedState fss = mSavedState.get(position);
+ if (fss != null) {
+ fragment.setInitialSavedState(fss);
+ }
+ }
+ while (mFragments.size() <= position) {
+ mFragments.add(null);
+ }
+ fragment.setMenuVisibility(false);
+ fragment.setUserVisibleHint(false);
+ mFragments.set(position, fragment);
+ mCurTransaction.add(container.getId(), fragment);
+
+ return fragment;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ Fragment fragment = (Fragment)object;
+
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+ if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ + " v=" + ((Fragment)object).getView());
+ while (mSavedState.size() <= position) {
+ mSavedState.add(null);
+ }
+ mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
+
+ // Only set the position to null if the fragment being removed is at "position"
+ // We do this because checkForIdChanges updates the mFragments list and if a fragment
+ // was removed then the fragment at "position" is not the fragment that was removed.
+ if (position < mFragments.size() && fragment == mFragments.get(position)) {
+ mFragments.set(position, null);
+ }
+ mCurTransaction.remove(fragment);
+ }
+
+ @Override
+ public void setPrimaryItem(ViewGroup container, int position, Object object) {
+ Fragment fragment = (Fragment)object;
+ if (fragment != mCurrentPrimaryItem) {
+ if (mCurrentPrimaryItem != null) {
+ mCurrentPrimaryItem.setMenuVisibility(false);
+ mCurrentPrimaryItem.setUserVisibleHint(false);
+ }
+ if (fragment != null) {
+ fragment.setMenuVisibility(true);
+ fragment.setUserVisibleHint(true);
+ }
+ mCurrentPrimaryItem = fragment;
+ }
+ }
+
+ @Override
+ public void finishUpdate(ViewGroup container) {
+ if (mCurTransaction != null) {
+ mCurTransaction.commitAllowingStateLoss();
+ mCurTransaction = null;
+ mFragmentManager.executePendingTransactions();
+ }
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return ((Fragment)object).getView() == view;
+ }
+
+ @Override
+ public Parcelable saveState() {
+ Bundle state = null;
+
+ mItemIds = new long[getCount()];
+ for (int i = 0; i < mItemIds.length; i++) {
+ mItemIds[i] = getItemId(i);
+ }
+ if (mSavedState.size() > 0) {
+ state = new Bundle();
+
+ if (mItemIds.length > 0) {
+ state.putLongArray("itemids", mItemIds);
+ }
+
+ Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
+ mSavedState.toArray(fss);
+ state.putParcelableArray("states", fss);
+ }
+ for (int i=0; i<mFragments.size(); i++) {
+ Fragment f = mFragments.get(i);
+ if (f != null) {
+ if (state == null) {
+ state = new Bundle();
+ }
+ String key = "f" + i;
+ mFragmentManager.putFragment(state, key, f);
+ }
+ }
+ return state;
+ }
+
+ @Override
+ public void restoreState(Parcelable state, ClassLoader loader) {
+ if (state != null) {
+ Bundle bundle = (Bundle)state;
+ bundle.setClassLoader(loader);
+
+ mItemIds = bundle.getLongArray("itemids");
+ if (mItemIds == null) {
+ mItemIds = new long[] {};
+ }
+
+ Parcelable[] fss = bundle.getParcelableArray("states");
+ mSavedState.clear();
+ mFragments.clear();
+ if (fss != null) {
+ for (int i=0; i<fss.length; i++) {
+ mSavedState.add((Fragment.SavedState)fss[i]);
+ }
+ }
+ Iterable<String> keys = bundle.keySet();
+ for (String key: keys) {
+ if (key.startsWith("f")) {
+ int index = Integer.parseInt(key.substring(1));
+ Fragment f = mFragmentManager.getFragment(bundle, key);
+ if (f != null) {
+ while (mFragments.size() <= index) {
+ mFragments.add(null);
+ }
+ f.setMenuVisibility(false);
+ mFragments.set(index, f);
+ } else {
+ Log.w(TAG, "Bad fragment at key " + key);
+ }
+ }
+ }
+ checkForIdChanges();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/org/cyanogenmod/theme/chooser2/NotificationHijackingService.java b/src/org/cyanogenmod/theme/chooser2/NotificationHijackingService.java
new file mode 100644
index 0000000..cee1b4f
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/NotificationHijackingService.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+
+public class NotificationHijackingService extends NotificationListenerService {
+ private static final String TAG = NotificationHijackingService.class.getName();
+ private static final String GOOGLE_PLAY_PACKAGE_NAME = "com.android.vending";
+ private static final String ACTION_INSTALLED =
+ "com.android.vending.SUCCESSFULLY_INSTALLED_CLICKED";
+ private static final String EXTRA_PACKAGE_NAME = "package_name";
+
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn) {
+ if (GOOGLE_PLAY_PACKAGE_NAME.equals(sbn.getPackageName())) {
+ PendingIntent contentIntent = sbn.getNotification().contentIntent;
+ if (contentIntent == null) return;
+ Intent intent = contentIntent.getIntent();
+ if (intent == null) return;
+ String action = intent.getAction();
+ if (ACTION_INSTALLED.equals(action)) {
+ String pkgName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+ try {
+ PackageInfo pi = getPackageManager().getPackageInfo(pkgName, 0);
+ if (pi != null) {
+ if (pi.themeInfo != null) {
+ cancelNotification(sbn.getKey());
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn) {
+ }
+
+ // ensure that this notification listener is enabled.
+ // the service watches for google play notifications
+ public static void ensureEnabled(Context context) {
+ ComponentName me = new ComponentName(context, NotificationHijackingService.class);
+ String meFlattened = me.flattenToString();
+
+ String existingListeners = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+
+ if (!TextUtils.isEmpty(existingListeners)) {
+ if (existingListeners.contains(meFlattened)) {
+ return;
+ } else {
+ existingListeners += ":" + meFlattened;
+ }
+ } else {
+ existingListeners = meFlattened;
+ }
+
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+ existingListeners);
+ }
+}
diff --git a/src/org/cyanogenmod/theme/chooser2/PagerContainer.java b/src/org/cyanogenmod/theme/chooser2/PagerContainer.java
new file mode 100644
index 0000000..a4349bd
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/PagerContainer.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.support.v4.view.ThemeViewPager;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+/**
+ * PagerContainer: A layout that displays a ViewPager with its children that are outside
+ * the typical pager bounds.
+ */
+public class PagerContainer extends FrameLayout implements ViewPager.OnPageChangeListener {
+ private static final int ANIMATE_OUT_DURATION = 300;
+ private static final int ANIMATE_OUT_INTERPOLATE_FACTOR = 1;
+ private static final int ANIMATE_IN_DURATION = 300;
+ private static final int ANIMATE_IN_INTERPOLATE_FACTOR = 2;
+
+ private ThemeViewPager mPager;
+ private Point mCenter = new Point();
+ private Point mInitialTouch = new Point();
+ private int mCollapsedHeight;
+ private boolean mIsAnimating = false;
+
+ boolean mNeedsRedraw = false;
+
+ public PagerContainer(Context context) {
+ this(context, null, 0);
+ }
+
+ public PagerContainer(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PagerContainer(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ mCollapsedHeight = generateLayoutParams(attrs).height;
+
+ //Disable clipping of children so non-selected pages are visible
+ setClipChildren(false);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ try {
+ mPager = (ThemeViewPager) getChildAt(0);
+ mPager.setOnPageChangeListener(this);
+ } catch (Exception e) {
+ throw new IllegalStateException("The root child of PagerContainer must be a ViewPager");
+ }
+ }
+
+ public ThemeViewPager getViewPager() {
+ return mPager;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ mCenter.x = w / 2;
+ mCenter.y = h / 2;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mIsAnimating) return true;
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // Do not allow touch events to propagate if we are animating
+ if (mIsAnimating) return true;
+
+ //We capture any touches not already handled by the ViewPager
+ // to implement scrolling from a touch outside the pager bounds.
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mInitialTouch.x = (int)ev.getX();
+ mInitialTouch.y = (int)ev.getY();
+ default:
+ ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y - mInitialTouch.y);
+ break;
+ }
+
+ return mPager.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ //Force the container to redraw on scrolling.
+ //Without this the outer pages render initially and then stay static
+ if (mNeedsRedraw) invalidate();
+ }
+
+ @Override
+ public void onPageSelected(int position) { }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ mNeedsRedraw = (state != ThemeViewPager.SCROLL_STATE_IDLE);
+ }
+
+ public void setIsAnimating(boolean isAnimating) {
+ mIsAnimating = isAnimating;
+ }
+
+ public void expand() {
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(getLayoutParams());
+ params.height = LinearLayout.LayoutParams.MATCH_PARENT;
+ setLayoutParams(params);
+
+ mPager.setExpanded(true);
+
+ final int current = mPager.getCurrentItem();
+ final int prevY = (int) getY();
+
+ //Since our viewpager's width is changing to fill the screen
+ //we must start the left/right children of the current page inwards on first draw
+ final int lChildPrevXf;
+ final int rChildPrevXf;
+
+ if (current != 0) {
+ final View lchild = mPager.getViewForPosition(current - 1);
+ lChildPrevXf = (int) lchild.getX();
+ } else {
+ lChildPrevXf = 0;
+ }
+
+ if (current < mPager.getAdapter().getCount() - 1) {
+ View rchild = mPager.getViewForPosition(current + 1);
+ rChildPrevXf = (int) rchild.getX();
+ } else {
+ rChildPrevXf = 0;
+ }
+
+
+ final ViewTreeObserver observer = mPager.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ observer.removeOnPreDrawListener(this);
+ if (current != 0) {
+ View lchild = mPager.getViewForPosition(current - 1);
+ lchild.setTranslationY(prevY - getY());
+ lchild.setX(lChildPrevXf);
+ animateChildOut(lchild, -getWidth());
+ }
+
+ if (current < mPager.getAdapter().getCount() - 1) {
+ View rchild = mPager.getViewForPosition(current + 1);
+ rchild.setX(rChildPrevXf);
+ rchild.setTranslationY(prevY - getY());
+ animateChildOut(rchild, getWidth());
+ }
+ return false;
+ }
+ });
+ }
+
+ public void collapse() {
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(getLayoutParams());
+ params.height = mCollapsedHeight;
+ setLayoutParams(params);
+
+ mPager.setExpanded(false);
+ int current = mPager.getCurrentItem();
+ final int prevY = (int) getY();
+
+ if (current != 0) {
+ View lchild = mPager.getViewForPosition(current - 1);
+ lchild.setTranslationY(0);
+ animateChildIn(lchild);
+ }
+
+ if (current < mPager.getAdapter().getCount() - 1) {
+ View rchild = mPager.getViewForPosition(current + 1);
+ rchild.setTranslationY(0);
+ animateChildIn(rchild);
+ }
+ }
+
+ private void animateChildOut(final View v, float endX) {
+ v.animate()
+ .translationX(endX)
+ .setDuration(ANIMATE_OUT_DURATION)
+ .setInterpolator(new AccelerateInterpolator(ANIMATE_OUT_INTERPOLATE_FACTOR));
+ }
+
+ private void animateChildIn(final View v) {
+ v.animate()
+ .translationX(0)
+ .setDuration(ANIMATE_IN_DURATION)
+ .setInterpolator(new DecelerateInterpolator(ANIMATE_IN_INTERPOLATE_FACTOR));
+ }
+} \ No newline at end of file
diff --git a/src/org/cyanogenmod/theme/chooser2/ThemeFragment.java b/src/org/cyanogenmod/theme/chooser2/ThemeFragment.java
new file mode 100644
index 0000000..6a73598
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/ThemeFragment.java
@@ -0,0 +1,3059 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.IntEvaluator;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.WallpaperManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.ThemeConfig;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.media.MediaPlayer;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.MutableLong;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.Animation;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.ScaleAnimation;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.PopupMenu;
+import android.widget.ProgressBar;
+import android.widget.Space;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.cyanogenmod.theme.chooser2.ComponentSelector.OnItemClickedListener;
+import org.cyanogenmod.theme.util.AudioUtils;
+import org.cyanogenmod.theme.util.BootAnimationHelper;
+import org.cyanogenmod.theme.util.CursorLoaderHelper;
+import org.cyanogenmod.theme.util.IconPreviewHelper;
+import org.cyanogenmod.theme.util.PreferenceUtils;
+import org.cyanogenmod.theme.util.ThemedTypefaceHelper;
+import org.cyanogenmod.theme.util.TypefaceHelperCache;
+import org.cyanogenmod.theme.util.Utils;
+import org.cyanogenmod.theme.util.WallpaperUtils;
+import org.cyanogenmod.theme.widget.BootAniImageView;
+import org.cyanogenmod.theme.widget.ConfirmCancelOverlay;
+import org.cyanogenmod.theme.widget.LockableScrollView;
+import org.cyanogenmod.theme.widget.ThemeTagLayout;
+
+import cyanogenmod.app.ThemeVersion;
+import cyanogenmod.providers.CMSettings;
+import cyanogenmod.providers.ThemesContract.PreviewColumns;
+import cyanogenmod.providers.ThemesContract.ThemesColumns;
+import cyanogenmod.themes.ThemeChangeRequest;
+import cyanogenmod.themes.ThemeChangeRequest.RequestType;
+import cyanogenmod.themes.ThemeManager;
+
+import org.cyanogenmod.internal.util.CmLockPatternUtils;
+import org.cyanogenmod.internal.util.ThemeUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipFile;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_ALARMS;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_BOOT_ANIM;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_LAUNCHER;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_LOCKSCREEN;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_NOTIFICATIONS;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_OVERLAYS;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_RINGTONES;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_STATUS_BAR;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_NAVIGATION_BAR;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_ICONS;
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.MODIFIES_FONTS;
+
+import static org.cyanogenmod.theme.chooser2.ComponentSelector.DEFAULT_COMPONENT_ID;
+
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_INVALID;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_ALL;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_STATUS_BAR;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_FONT;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_ICONS;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_WALLPAPER;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_NAVIGATION_BAR;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_LOCKSCREEN;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_STYLE;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_BOOT_ANIMATION;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_RINGTONE;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_NOTIFICATION;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_ALARM;
+import static org.cyanogenmod.theme.util.CursorLoaderHelper.LOADER_ID_LIVE_LOCK_SCREEN;
+
+import static cyanogenmod.providers.CMSettings.Secure.LIVE_LOCK_SCREEN_ENABLED;
+
+import static org.cyanogenmod.internal.util.ThemeUtils.SYSTEM_TARGET_API;
+
+public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>,
+ ThemeManager.ThemeChangeListener, ThemeManager.ThemeProcessingListener {
+ private static final String TAG = ThemeFragment.class.getSimpleName();
+
+ public static final int ANIMATE_START_DELAY = 200;
+ public static final int ANIMATE_DURATION = 300;
+ public static final int ANIMATE_INTERPOLATE_FACTOR = 3;
+ public static final int ANIMATE_COMPONENT_CHANGE_DURATION = 200;
+ public static final int ANIMATE_COMPONENT_ICON_DELAY = 50;
+ public static final int ANIMATE_PROGRESS_IN_DURATION = 500;
+ public static final int ANIMATE_TITLE_OUT_DURATION = 400;
+ 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 String CURRENTLY_APPLIED_THEME = "currently_applied_theme";
+
+ private static final ComponentName COMPONENT_DIALER =
+ new ComponentName("com.android.dialer", "com.android.dialer.DialtactsActivity");
+ private static final ComponentName COMPONENT_DIALERNEXT =
+ new ComponentName("com.cyngn.dialer", "com.android.dialer.DialtactsActivity");
+ private static final ComponentName COMPONENT_MESSAGING =
+ new ComponentName("com.android.mms", "com.android.mms.ui.ConversationList");
+ private static final ComponentName COMPONENT_CAMERANEXT =
+ new ComponentName("com.cyngn.cameranext", "com.android.camera.CameraLauncher");
+ private static final ComponentName COMPONENT_CAMERA =
+ new ComponentName("com.android.camera2", "com.android.camera.CameraActivity");
+ private static final ComponentName COMPONENT_BROWSER =
+ new ComponentName("com.android.browser", "com.android.browser.BrowserActivity");
+ private static final ComponentName COMPONENT_SETTINGS =
+ new ComponentName("com.android.settings", "com.android.settings.Settings");
+ private static final ComponentName COMPONENT_CALENDAR =
+ new ComponentName("com.android.calendar", "com.android.calendar.AllInOneActivity");
+ private static final ComponentName COMPONENT_GALERY =
+ new ComponentName("com.android.gallery3d", "com.android.gallery3d.app.GalleryActivity");
+
+ private static final String CAMERA_NEXT_PACKAGE = "com.cyngn.cameranext";
+ private static final String DIALER_NEXT_PACKAGE = "com.cyngn.dialer";
+
+ private static final int ADDITIONAL_CONTENT_SPACE_ID = 123456;
+ private static final long SLIDE_CONTENT_ANIM_DURATION = 300L;
+ private static final long LOCK_SCREEN_CARD_SCROLL_ANIMATION_DURATION = 400;
+ private static final long SHOW_LOCK_SCREEN_CARD_DELAY = 500;
+
+ private static final int DEFAULT_WIFI_MARGIN = 0;
+ private static final int DEFAULT_CLOCK_COLOR = Color.WHITE;
+
+ protected static final String WALLPAPER_NONE = "";
+ protected static final String LOCKSCREEN_NONE = "";
+
+ protected static final String ARG_PACKAGE_NAME = "pkgName";
+ protected static final String ARG_COMPONENT_ID = "cmpntId";
+ protected static final String ARG_SKIP_LOADING_ANIM = "skipLoadingAnim";
+ protected static final String ARG_ANIMATE_TO_LOCK_SCREEN_CARD = "animateToLockScreenCard";
+
+ private static final String LLS_PACKAGE_NAME = "com.cyngn.lockscreen.live";
+ private static final String LLS_PROVIDER_NAME =
+ "com.cyngn.lockscreen.live.LockScreenProviderService";
+
+ private static final int PERMISSION_REQUEST = 100;
+
+ protected static ComponentName[] sIconComponents;
+
+ protected static TypefaceHelperCache sTypefaceHelperCache;
+
+ /**
+ * Maps the card's resource ID to a theme component
+ */
+ private final SparseArray<String> mCardIdsToComponentTypes = new SparseArray<String>();
+
+ protected String mPkgName;
+ protected Typeface mTypefaceNormal;
+ protected int mBatteryStyle;
+
+ protected LockableScrollView mScrollView;
+ protected ViewGroup mScrollContent;
+ protected ViewGroup mPreviewContent; // Contains icons, font, nav/status etc. Not wallpaper
+ protected View mLoadingView;
+
+ //Status Bar Views
+ protected ComponentCardView mStatusBarCard;
+ protected ImageView mBluetooth;
+ protected ImageView mWifi;
+ protected ImageView mSignal;
+ protected ImageView mBattery;
+ protected TextView mClock;
+
+ // Other Misc Preview Views
+ protected FrameLayout mShadowFrame;
+ protected ImageView mWallpaper;
+ protected ViewGroup mStatusBar;
+ protected TextView mFontPreview;
+ protected ComponentCardView mStyleCard;
+ protected ComponentCardView mFontCard;
+ protected ComponentCardView mIconCard;
+ protected ComponentCardView mBootAnimationCard;
+ protected BootAniImageView mBootAnimation;
+
+ // Nav Bar Views
+ protected ComponentCardView mNavBarCard;
+ protected ViewGroup mNavBar;
+ protected ImageView mBackButton;
+ protected ImageView mHomeButton;
+ protected ImageView mRecentButton;
+
+ // Title Card Views
+ protected ViewGroup mTitleCard;
+ protected ViewGroup mTitleLayout;
+ protected TextView mTitle;
+ protected TextView mAuthor;
+ protected ImageView mCustomize;
+ protected ImageView mOverflow;
+ protected ImageView mDelete;
+ protected ImageView mReset;
+ protected ProgressBar mProgress;
+
+ // Additional Card Views
+ protected LinearLayout mAdditionalCards;
+ protected WallpaperCardView mWallpaperCard;
+ protected WallpaperCardView mLockScreenCard;
+
+ // Style views
+ protected ImageView mStylePreview;
+
+ // Sound cards
+ protected ComponentCardView mRingtoneCard;
+ protected ImageView mRingtonePlayPause;
+ protected ComponentCardView mNotificationCard;
+ protected ImageView mNotificationPlayPause;
+ protected ComponentCardView mAlarmCard;
+ protected ImageView mAlarmPlayPause;
+ protected Map<ImageView, MediaPlayer> mMediaPlayers;
+
+ protected Handler mHandler;
+
+ protected int mActiveCardId = -1;
+ protected ComponentSelector mSelector;
+ // Supported components for the theme this fragment represents
+ protected Map<String, String> mSelectedComponentsMap = new HashMap<String, String>();
+ protected Long mSelectedWallpaperComponentId;
+ // Current system theme configuration as component -> pkgName
+ protected Map<String, String> mCurrentTheme = new HashMap<String, String>();
+ protected MutableLong mCurrentWallpaperComponentId = new MutableLong(DEFAULT_COMPONENT_ID);
+ // Set of components available in the base theme
+ protected HashSet<String> mBaseThemeSupportedComponents = new HashSet<String>();
+ protected Cursor mCurrentCursor;
+ protected int mCurrentLoaderId;
+ protected boolean mThemeResetting;
+ protected boolean mSkipLoadingAnim;
+
+ // Accept/Cancel overlay
+ protected ConfirmCancelOverlay mConfirmCancelOverlay;
+
+ // Customize/Reset theme layout
+ protected View mCustomizeResetLayout;
+ protected View mResetButton;
+ protected View mCustomizeButton;
+ protected View mDismissButton;
+
+ // Processing theme layout
+ protected View mProcessingThemeLayout;
+
+ protected ThemeTagLayout mThemeTagLayout;
+
+ protected View mClickableView;
+ protected String mBaseThemePkgName;
+
+ protected Uri mExternalWallpaperUri;
+ protected Uri mExternalLockscreenUri;
+
+ protected boolean mExpanded;
+ protected boolean mProcessingResources;
+ protected boolean mApplyThemeOnPopulated;
+
+ protected boolean mIsLegacyTheme;
+
+ private Runnable mAfterPermissionGrantedRunnable;
+
+ private static final int mThemeVersion = ThemeVersion.getVersion();
+
+ protected boolean mShowLockScreenSelectorAfterContentLoaded;
+
+ protected enum CustomizeResetAction {
+ Customize,
+ Reset,
+ Dismiss
+ }
+
+ static ThemeFragment newInstance(String pkgName, boolean skipLoadingAnim) {
+ ThemeFragment f = new ThemeFragment();
+ Bundle args = new Bundle();
+ args.putString(ARG_PACKAGE_NAME, pkgName);
+ args.putBoolean(ARG_SKIP_LOADING_ANIM, skipLoadingAnim);
+ args.putLong(ARG_COMPONENT_ID, DEFAULT_COMPONENT_ID);
+ f.setArguments(args);
+ return f;
+ }
+
+ /**
+ * When creating, retrieve this instance's number from its arguments.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Context context = getActivity();
+ mPkgName = getArguments().getString(ARG_PACKAGE_NAME);
+ mSkipLoadingAnim = getArguments().getBoolean(ARG_SKIP_LOADING_ANIM);
+ // TODO: Load from settings once available
+ mBatteryStyle = 0;/*Settings.System.getInt(context.getContentResolver(),
+ Settings.System.STATUS_BAR_BATTERY_STYLE, 0);*/
+
+ getIconComponents(context);
+ if (sTypefaceHelperCache == null) {
+ sTypefaceHelperCache = TypefaceHelperCache.getInstance();
+ }
+ ThemedTypefaceHelper helper = sTypefaceHelperCache.getHelperForTheme(context, mPkgName);
+ mTypefaceNormal = helper.getTypeface(Typeface.NORMAL);
+
+ mHandler = new Handler();
+
+ mCardIdsToComponentTypes.put(R.id.status_bar_container, MODIFIES_STATUS_BAR);
+ mCardIdsToComponentTypes.put(R.id.font_preview_container, MODIFIES_FONTS);
+ mCardIdsToComponentTypes.put(R.id.icon_container, MODIFIES_ICONS);
+ mCardIdsToComponentTypes.put(R.id.navigation_bar_container, MODIFIES_NAVIGATION_BAR);
+ mCardIdsToComponentTypes.put(R.id.wallpaper_card, MODIFIES_LAUNCHER);
+ mCardIdsToComponentTypes.put(R.id.lockscreen_card, MODIFIES_LOCKSCREEN);
+ mCardIdsToComponentTypes.put(R.id.style_card, MODIFIES_OVERLAYS);
+ mCardIdsToComponentTypes.put(R.id.bootani_preview_container, MODIFIES_BOOT_ANIM);
+ mCardIdsToComponentTypes.put(R.id.ringtone_preview_container, MODIFIES_RINGTONES);
+ mCardIdsToComponentTypes.put(R.id.notification_preview_container, MODIFIES_NOTIFICATIONS);
+ mCardIdsToComponentTypes.put(R.id.alarm_preview_container, MODIFIES_ALARMS);
+
+ mMediaPlayers = new HashMap<ImageView, MediaPlayer>(3);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_pager_list, container, false);
+
+ mScrollView = (LockableScrollView) v.findViewById(android.R.id.list);
+ mScrollView.setScrollingEnabled(false);
+ mScrollContent = (ViewGroup) mScrollView.getChildAt(0);
+ mPreviewContent = (ViewGroup) v.findViewById(R.id.preview_container);
+ mLoadingView = v.findViewById(R.id.loading_view);
+ mThemeTagLayout = (ThemeTagLayout) v.findViewById(R.id.tag_layout);
+
+ // Status Bar
+ mStatusBarCard = (ComponentCardView) v.findViewById(R.id.status_bar_container);
+ mStatusBar = (ViewGroup) v.findViewById(R.id.status_bar);
+ mBluetooth = (ImageView) v.findViewById(R.id.bluetooth_icon);
+ mWifi = (ImageView) v.findViewById(R.id.wifi_icon);
+ mSignal = (ImageView) v.findViewById(R.id.signal_icon);
+ mBattery = (ImageView) v.findViewById(R.id.battery);
+ mClock = (TextView) v.findViewById(R.id.clock);
+
+ // Wallpaper / Font / Icons / etc
+ mWallpaper = (ImageView) v.findViewById(R.id.wallpaper);
+ mFontCard = (ComponentCardView) v.findViewById(R.id.font_preview_container);
+ mFontPreview = (TextView) v.findViewById(R.id.font_preview);
+ mFontPreview.setTypeface(mTypefaceNormal);
+ mIconCard = (ComponentCardView) v.findViewById(R.id.icon_container);
+ mShadowFrame = (FrameLayout) v.findViewById(R.id.shadow_frame);
+ mStyleCard = (ComponentCardView) v.findViewById(R.id.style_card);
+ mStylePreview = (ImageView) v.findViewById(R.id.style_preview);
+ mBootAnimationCard = (ComponentCardView) v.findViewById(R.id.bootani_preview_container);
+ mBootAnimation =
+ (BootAniImageView) mBootAnimationCard.findViewById(R.id.bootani_preview);
+ mRingtoneCard = (ComponentCardView) v.findViewById(R.id.ringtone_preview_container);
+ mRingtonePlayPause = (ImageView) mRingtoneCard.findViewById(R.id.play_pause);
+ mNotificationCard = (ComponentCardView) v.findViewById(R.id.notification_preview_container);
+ mNotificationPlayPause = (ImageView) mNotificationCard.findViewById(R.id.play_pause);
+ mAlarmCard = (ComponentCardView) v.findViewById(R.id.alarm_preview_container);
+ mAlarmPlayPause = (ImageView) mAlarmCard.findViewById(R.id.play_pause);
+
+ // Nav Bar
+ mNavBarCard = (ComponentCardView) v.findViewById(R.id.navigation_bar_container);
+ mNavBar = (ViewGroup) v.findViewById(R.id.navigation_bar);
+ mBackButton = (ImageView) v.findViewById(R.id.back_button);
+ mHomeButton = (ImageView) v.findViewById(R.id.home_button);
+ mRecentButton = (ImageView) v.findViewById(R.id.recent_button);
+
+ // Title Card
+ mTitleCard = (ViewGroup)v.findViewById(R.id.title_card);
+ mTitleLayout = (ViewGroup) v.findViewById(R.id.title_layout);
+ mTitle = (TextView) v.findViewById(R.id.title);
+ mAuthor = (TextView) v.findViewById(R.id.author);
+ mProgress = (ProgressBar) v.findViewById(R.id.apply_progress);
+ mOverflow = (ImageView) v.findViewById(R.id.overflow);
+ mOverflow.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (isShowingConfirmCancelOverlay()) {
+ hideConfirmCancelOverlay();
+ } else if (isShowingCustomizeResetLayout()) {
+ hideCustomizeResetLayout();
+ }
+
+ PopupMenu popupmenu = new PopupMenu(getActivity(), mTitleCard, Gravity.END);
+ popupmenu.getMenuInflater().inflate(R.menu.overflow, popupmenu.getMenu());
+
+ Menu menu = popupmenu.getMenu();
+ if (CURRENTLY_APPLIED_THEME.equals(mPkgName) ||
+ mPkgName.equals(Utils.getDefaultThemePackageName(getActivity())) ||
+ mPkgName.equals(ThemeConfig.SYSTEM_DEFAULT)) {
+ menu.findItem(R.id.menu_delete).setEnabled(false);
+ }
+ if (!mThemeTagLayout.isCustomizedTagEnabled()) {
+ menu.findItem(R.id.menu_reset).setVisible(false);
+ }
+
+ popupmenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ return onPopupMenuItemClick(item);
+ }
+ });
+ popupmenu.show();
+ }
+ });
+ mCustomize = (ImageView) v.findViewById(R.id.customize);
+ mCustomize.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if (!isShowingConfirmCancelOverlay() && !isShowingCustomizeResetLayout()) {
+ getChooserActivity().expand();
+ }
+ }
+ });
+
+ mDelete = (ImageView) v.findViewById(R.id.delete);
+ mDelete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showDeleteThemeOverlay();
+ }
+ });
+ if (Utils.getDefaultThemePackageName(getActivity()).equals(mPkgName) ||
+ ThemeConfig.SYSTEM_DEFAULT.equals(mPkgName)) {
+ mDelete.setVisibility(View.GONE);
+ }
+
+ mReset = (ImageView) v.findViewById(R.id.reset);
+ mReset.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showResetThemeOverlay();
+ }
+ });
+ mReset.setVisibility(View.GONE);
+
+ if (!Utils.hasNavigationBar(getActivity())) {
+ adjustScrollViewPaddingTop();
+ mNavBarCard.setVisibility(View.GONE);
+ }
+
+ // Additional cards which should hang out offscreen until expanded
+ mAdditionalCards = (LinearLayout) v.findViewById(R.id.additional_cards);
+
+ mWallpaperCard = (WallpaperCardView) v.findViewById(R.id.wallpaper_card);
+ mLockScreenCard = (WallpaperCardView) v.findViewById(R.id.lockscreen_card);
+ int translationY = getDistanceToMoveBelowScreen(mAdditionalCards);
+ mAdditionalCards.setTranslationY(translationY);
+
+ mConfirmCancelOverlay = (ConfirmCancelOverlay) v.findViewById(R.id.confirm_cancel_overlay);
+ mClickableView = v.findViewById(R.id.clickable_view);
+
+ mCustomizeResetLayout = v.findViewById(R.id.customize_reset_theme_layout);
+ mDismissButton = mCustomizeResetLayout.findViewById(R.id.btn_dismiss);
+ mDismissButton.setOnClickListener(mCustomizeResetClickListener);
+ mResetButton = mCustomizeResetLayout.findViewById(R.id.btn_reset);
+ mResetButton.setOnClickListener(mCustomizeResetClickListener);
+ mCustomizeButton = mCustomizeResetLayout.findViewById(R.id.btn_customize);
+ mCustomizeButton.setOnClickListener(mCustomizeResetClickListener);
+
+ mProcessingThemeLayout = v.findViewById(R.id.processing_theme_layout);
+
+ if (mPkgName.equals(Utils.getDefaultThemePackageName(getActivity()))) {
+ mThemeTagLayout.setDefaultTagEnabled(true);
+ }
+ if (PreferenceUtils.hasThemeBeenUpdated(getActivity(), mPkgName)) {
+ mThemeTagLayout.setUpdatedTagEnabled(true);
+ }
+
+ if (mSkipLoadingAnim) {
+ mLoadingView.setVisibility(View.GONE);
+ mTitleLayout.setAlpha(1f);
+ }
+
+ getLoaderManager().initLoader(LOADER_ID_ALL, null, this);
+
+ setupCardClickListeners(v);
+
+ return v;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ stopMediaPlayers();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ ThemeManager tm = getThemeManager();
+ if (tm != null) {
+ if (isThemeProcessing()) {
+ tm.registerProcessingListener(this);
+ mProcessingThemeLayout.setVisibility(View.VISIBLE);
+ mCustomize.setVisibility(View.INVISIBLE);
+ mCustomize.setAlpha(0f);
+ if (mDelete.getVisibility() != View.GONE) {
+ mDelete.setVisibility(View.INVISIBLE);
+ mDelete.setAlpha(0f);
+ }
+ mProcessingResources = true;
+ } else {
+ mCustomize.setVisibility(View.VISIBLE);
+ mCustomize.setAlpha(1f);
+ if (mDelete.getVisibility() != View.GONE) {
+ mDelete.setVisibility(View.VISIBLE);
+ mDelete.setAlpha(1f);
+ }
+ mProcessingResources = false;
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ freeMediaPlayers();
+ ThemeManager tm = getThemeManager();
+ if (tm != null) {
+ tm.removeClient(this);
+ tm.unregisterProcessingListener(this);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ if (requestCode == PERMISSION_REQUEST) {
+ int N = permissions.length;
+ for (int i = 0; i < N; i++) {
+ if (READ_EXTERNAL_STORAGE.equals(permissions[i])) {
+ if (grantResults[i] == PERMISSION_GRANTED) {
+ // Run the runnable now that we have been granted permission
+ if (mAfterPermissionGrantedRunnable != null) {
+ mAfterPermissionGrantedRunnable.run();
+ mAfterPermissionGrantedRunnable = null;
+ }
+ } else {
+ // inform the user that they will be unable to pick an image because
+ // we were not granted permission to do so
+ Toast.makeText(getActivity(),
+ R.string.read_external_permission_denied_message,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onProgress(int progress) {
+ mProgress.setProgress(progress);
+ }
+
+ private void setLiveLockScreenAsKeyguard(boolean setLLS) {
+ ComponentName cn = null;
+ if (setLLS) {
+ try {
+ final String[] permissions = Utils.getDangerousPermissionsNotGranted(getActivity(),
+ LLS_PACKAGE_NAME);
+ if (permissions.length > 0) {
+ Intent reqIntent = Utils.buildPermissionGrantRequestIntent(getActivity(),
+ LLS_PACKAGE_NAME, permissions);
+ if (reqIntent != null) {
+ startActivity(reqIntent);
+ }
+ }
+ cn = new ComponentName(LLS_PACKAGE_NAME, LLS_PROVIDER_NAME);
+ } catch (InvalidParameterException e) {
+ Log.e(TAG, "Package Manager couldn't find package " + LLS_PACKAGE_NAME, e);
+ return;
+ }
+ }
+
+ CmLockPatternUtils lockPatternUtils = new CmLockPatternUtils(getActivity());
+ try {
+ lockPatternUtils.setThirdPartyKeyguard(cn);
+ } catch (PackageManager.NameNotFoundException e) {
+ // we should not be here!
+ }
+ }
+
+ @Override
+ public void onFinish(boolean isSuccess) {
+ // We post a runnable to mHandler so the client is removed from the same thread
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ ThemeManager tm = getThemeManager();
+ if (tm != null) tm.removeClient(ThemeFragment.this);
+ }
+ });
+ if (isSuccess) {
+ if (mExternalLockscreenUri != null) {
+ // Handle setting an external wallpaper in a separate thread
+ // Need to do this AFTER ThemeMgr is done processing our change request.
+ // The external lock screen that we just applied would be removed when
+ // the change request is setting/clearing the lock screen
+ new Thread(mApplyExternalLockscreenRunnable).start();
+ }
+ Map<String, String> appliedComponents = getComponentsToApply();
+ boolean modLLS = appliedComponents.containsKey(MODIFIES_LIVE_LOCK_SCREEN);
+ if (modLLS) {
+ String pkgName = appliedComponents.get(MODIFIES_LIVE_LOCK_SCREEN);
+ if (pkgName.equals(LOCKSCREEN_NONE)) {
+ setLiveLockScreenAsKeyguard(false);
+ } else {
+ setLiveLockScreenAsKeyguard(true);
+ }
+ }
+ mProgress.setProgress(100);
+ animateProgressOut();
+ }
+ getChooserActivity().themeChangeEnd(isSuccess);
+ }
+
+ @Override
+ public void onFinishedProcessing(String pkgName) {
+ if (pkgName.equals(mPkgName) || pkgName.equals(mBaseThemePkgName)) {
+ ThemeManager tm = getThemeManager();
+ if (tm != null) {
+ tm.unregisterProcessingListener(this);
+ }
+ }
+ }
+
+ @Override
+ public void setUserVisibleHint(boolean isVisibleToUser) {
+ super.setUserVisibleHint(isVisibleToUser);
+ if (mThemeTagLayout == null) return;
+
+ if (!isVisibleToUser) {
+ if (PreferenceUtils.hasThemeBeenUpdated(getActivity(), mPkgName)) {
+ mThemeTagLayout.setUpdatedTagEnabled(true);
+ }
+ } else {
+ if (PreferenceUtils.hasThemeBeenUpdated(getActivity(), mPkgName)) {
+ PreferenceUtils.removeUpdatedTheme(getActivity(), mPkgName);
+ }
+ }
+ }
+
+ 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);
+ if (mLockScreenCard.isShowingEmptyView()) {
+ mLockScreenCard.setEmptyViewEnabled(false);
+ }
+ mLockScreenCard.setWallpaper(wp);
+ // remove the entry from mSelectedComponentsMap
+ mSelectedComponentsMap.remove(ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN);
+ 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();
+ }
+
+ private void adjustScrollViewPaddingTop() {
+ Resources res = getResources();
+ int extraPadding =
+ res.getDimensionPixelSize(R.dimen.navigation_bar_height) / 2;
+ mScrollView.setPadding(mScrollView.getPaddingLeft(),
+ mScrollView.getPaddingTop() + extraPadding, mScrollView.getPaddingRight(),
+ mScrollView.getPaddingBottom());
+ }
+
+ protected boolean isThemeProcessing() {
+ ThemeManager tm = getThemeManager();
+ if (tm != null) {
+ final String pkgName = mBaseThemePkgName != null ? mBaseThemePkgName : mPkgName;
+ return tm.isThemeBeingProcessed(pkgName);
+ }
+ return false;
+ }
+
+ protected boolean onPopupMenuItemClick(MenuItem item) {
+ switch(item.getItemId()) {
+ /* TODO: Add back in once there is UX available for this feature
+ case R.id.menu_author:
+ Toast.makeText(getActivity(),
+ "Not supported",
+ Toast.LENGTH_LONG).show();
+ break;
+ */
+ case R.id.menu_delete:
+ showDeleteThemeOverlay();
+ break;
+ }
+
+ return true;
+ }
+
+ public void expand() {
+ if (mCurrentLoaderId == LOADER_ID_ALL && mCurrentCursor != null) {
+ loadAdditionalCards(mCurrentCursor);
+ // we don't need this now that the additional cards are loaded, and
+ // we don't want to re-load these cards if the we expand again.
+ mCurrentCursor = null;
+ }
+ mClickableView.setVisibility(View.GONE);
+ mScrollView.setScrollingEnabled(true);
+ // Full width and height!
+ ViewGroup content = (ViewGroup) mScrollView.getParent();
+ content.setPadding(0, 0, 0, 0);
+ ViewGroup.LayoutParams layoutParams = mPreviewContent.getLayoutParams();
+ layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ mPreviewContent.setLayoutParams(layoutParams);
+ mScrollView.setPadding(0,0,0,0);
+
+ // The parent of the wallpaper squishes the wp slightly because of padding from the 9 patch
+ // When the parent expands, the wallpaper returns to regular size which creates an
+ // undesireable effect.
+ Rect padding = new Rect();
+ NinePatchDrawable bg = (NinePatchDrawable) mShadowFrame.getBackground();
+ bg.getPadding(padding);
+ mIconCard.setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ mShadowFrame.setBackground(null);
+ mShadowFrame.setPadding(0, 0, 0, 0);
+
+ // Off screen cards will become visible and then be animated in
+ mWallpaperCard.setVisibility(View.VISIBLE);
+
+ // Expand the children
+ int top = (int) getResources()
+ .getDimension(R.dimen.expanded_card_margin_top);
+ for (int i = 0; i < mPreviewContent.getChildCount(); i++) {
+ ComponentCardView child = (ComponentCardView) mPreviewContent.getChildAt(i);
+
+ LinearLayout.LayoutParams lparams =
+ (LinearLayout.LayoutParams) child.getLayoutParams();
+ if (child == mStatusBarCard) {
+ int statusBarHeight = getResources()
+ .getDimensionPixelSize(R.dimen.status_bar_height);
+ lparams.setMargins(0, top + statusBarHeight, 0, 0);
+ } else {
+ lparams.setMargins(0, top, 0, 0);
+ }
+
+ child.setLayoutParams(lparams);
+ child.expand(false);
+ }
+
+ // Expand the additional children.
+ mAdditionalCards.setVisibility(View.VISIBLE);
+ for (int i = 0; i < mAdditionalCards.getChildCount(); i++) {
+ View v = mAdditionalCards.getChildAt(i);
+ if (v instanceof ComponentCardView) {
+ ComponentCardView card = (ComponentCardView) v;
+ card.setVisibility(View.VISIBLE);
+ card.expand(true);
+ }
+ }
+
+ // Collect the present position of all the children. The next layout/draw cycle will
+ // change these bounds since we just expanded them. Then we can animate from prev location
+ // to the new location. Note that the order of these calls matter as they all
+ // add themselves to the root layout as overlays
+ mScrollView.requestLayout();
+ animateWallpaperOut();
+ animateTitleCard(true, false);
+ animateChildren(true, getChildrensGlobalBounds(mPreviewContent));
+ animateExtras(true);
+ mSelector = getChooserActivity().getComponentSelector();
+ mSelector.setOnItemClickedListener(mOnComponentItemClicked);
+ if (mBootAnimation != null) mBootAnimation.start();
+ hideThemeTagLayout();
+ mExpanded = true;
+ }
+
+
+
+ // Returns the boundaries for all the children of parent relative to the app window
+ private List<Rect> getChildrensGlobalBounds(ViewGroup parent) {
+ List<Rect> bounds = new ArrayList<Rect>();
+ for (int i = 0; i < parent.getChildCount(); i++) {
+ final View v = parent.getChildAt(i);
+ int[] pos = new int[2];
+ v.getLocationInWindow(pos);
+ Rect boundary = new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1]+v.getHeight());
+ bounds.add(boundary);
+ }
+ return bounds;
+ }
+
+ public void performClick(boolean clickedOnContent) {
+ // Don't do anything if the theme is being processed
+ if (mProcessingThemeLayout.getVisibility() == View.VISIBLE) return;
+
+ if (clickedOnContent) {
+ showApplyThemeOverlay();
+ } else {
+ if (isShowingConfirmCancelOverlay()) {
+ hideConfirmCancelOverlay();
+ }
+ }
+ }
+
+ public void fadeOutCards(Runnable endAction) {
+ for (int i = 0; i < mPreviewContent.getChildCount(); i++) {
+ ComponentCardView v = (ComponentCardView) mPreviewContent.getChildAt(i);
+ v.animateFadeOut();
+ }
+ mHandler.postDelayed(endAction, ComponentCardView.CARD_FADE_DURATION);
+ }
+
+ public void collapse(final boolean applyTheme) {
+ mScrollView.setScrollingEnabled(false);
+
+ // Pad the view so it appears thinner
+ ViewGroup content = (ViewGroup) mScrollView.getParent();
+ Resources r = mScrollView.getContext().getResources();
+ int leftRightPadding = (int) r.getDimension(R.dimen.collapsed_theme_page_padding);
+ content.setPadding(leftRightPadding, 0, leftRightPadding, 0);
+
+ if (applyTheme) {
+ final boolean customized = isThemeCustomized();
+ mThemeTagLayout.setCustomizedTagEnabled(customized);
+ mReset.setVisibility(customized ? View.VISIBLE : View.GONE);
+ }
+
+ //Move the theme preview so that it is near the center of page per spec
+ int paddingTop = (int) r.getDimension(R.dimen.collapsed_theme_page_padding_top);
+ if (!Utils.hasNavigationBar(getActivity())) {
+ paddingTop +=
+ r.getDimensionPixelSize(R.dimen.navigation_bar_height) / 2;
+ }
+ mScrollView.setPadding(0, paddingTop, 0, 0);
+
+ // During expand the wallpaper size decreases slightly to makeup for 9patch padding
+ // so when we collapse we should increase it again.
+ mShadowFrame.setBackgroundResource(R.drawable.bg_themepreview_shadow);
+ Rect padding = new Rect();
+ final NinePatchDrawable bg = (NinePatchDrawable) mShadowFrame.getBackground();
+ bg.getPadding(padding);
+ mShadowFrame.setPadding(padding.left, padding.top, padding.right, padding.bottom);
+
+ // Gradually fade the drop shadow back in or else it will be out of place
+ ValueAnimator shadowAnimation = ValueAnimator.ofObject(new IntEvaluator(), 0, 255);
+ shadowAnimation.setDuration(ANIMATE_DURATION);
+ shadowAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ bg.setAlpha((Integer) animator.getAnimatedValue());
+ }
+
+ });
+ shadowAnimation.start();
+
+ //Move the title card back in
+ mTitleCard.setVisibility(View.VISIBLE);
+ mTitleCard.setTranslationY(0);
+
+ // Shrink the height
+ ViewGroup.LayoutParams layoutParams = mPreviewContent.getLayoutParams();
+ Resources resources = mPreviewContent.getResources();
+ layoutParams.height = (int) resources.getDimension(R.dimen.theme_preview_height);
+
+ mScrollView.requestLayout();
+ List<Rect> bounds = getChildrensGlobalBounds(mPreviewContent);
+ for (int i = 0; i < mPreviewContent.getChildCount(); i++) {
+ ComponentCardView child = (ComponentCardView) mPreviewContent.getChildAt(i);
+ LinearLayout.LayoutParams lparams =
+ (LinearLayout.LayoutParams) child.getLayoutParams();
+ lparams.setMargins(0, 0, 0, 0);
+
+ if (child.getId() == R.id.icon_container) {
+ int top = (int) child.getResources()
+ .getDimension(R.dimen.collapsed_icon_card_margin_top);
+ lparams.setMargins(0, top, 0, 0);
+ } else if (child.getId() == R.id.font_preview_container) {
+ int top = (int) child.getResources()
+ .getDimension(R.dimen.collapsed_font_card_margin_top);
+ lparams.setMargins(0, top, 0, 0);
+ } else if (child.getId() == R.id.navigation_bar_container) {
+ int top = (int) child.getResources()
+ .getDimension(R.dimen.collapsed_navbar_card_margin_top);
+ lparams.setMargins(0, top, 0, 0);
+ }
+
+ child.getLayoutParams();
+ child.collapse();
+ }
+
+ // Collapse additional cards
+ for (int i = 0; i < mAdditionalCards.getChildCount(); i++) {
+ View v = mAdditionalCards.getChildAt(i);
+ if (v instanceof ComponentCardView) {
+ ComponentCardView card = (ComponentCardView) v;
+ card.setVisibility(View.VISIBLE);
+ card.collapse();
+ }
+ }
+
+ animateChildren(false, bounds);
+ animateExtras(false);
+ animateWallpaperIn();
+ animateTitleCard(false, applyTheme);
+ if (mBootAnimation != null) mBootAnimation.stop();
+ stopMediaPlayers();
+ showThemeTagLayout();
+
+ // Need to set the wallpaper background to black if the user has selected to apply
+ // the "none" wallpaper
+ if (applyTheme) {
+ String pkgName = mSelectedComponentsMap.get(ThemesColumns.MODIFIES_LAUNCHER);
+ 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();
+ }
+ }
+ mExpanded = false;
+ }
+
+ // This will animate the children's vertical positions between the previous bounds and the
+ // new bounds which occur on the next draw
+ private void animateChildren(final boolean isExpanding, final List<Rect> prevBounds) {
+ final ViewGroup root = (ViewGroup) getActivity().getWindow()
+ .getDecorView().findViewById(android.R.id.content);
+
+ final Resources res = getResources();
+ final float yOffset =
+ res.getDimensionPixelSize(R.dimen.expand_collapse_child_offset)
+ * (isExpanding ? -1 : 1);
+ // Grab the child's new location and animate from prev to current loc.
+ final ViewTreeObserver observer = mScrollContent.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ observer.removeOnPreDrawListener(this);
+
+ for (int i = mPreviewContent.getChildCount() - 1; i >= 0; i--) {
+ final ComponentCardView v = (ComponentCardView) mPreviewContent.getChildAt(i);
+
+ float prevY;
+ float endY;
+ float prevHeight;
+ float endHeight;
+ if (i >= prevBounds.size()) {
+ // View is being created
+ prevY = mPreviewContent.getTop() + mPreviewContent.getHeight();
+ endY = v.getY();
+ prevHeight = v.getHeight();
+ endHeight = v.getHeight();
+ } else {
+ Rect boundary = prevBounds.get(i);
+ prevY = boundary.top;
+ prevHeight = boundary.height();
+
+ int[] endPos = new int[2];
+ v.getLocationInWindow(endPos);
+ endY = endPos[1];
+ endHeight = v.getHeight();
+ }
+
+ int paddingTop = v.getPaddingTop() / 2;
+ float dy = (prevY - endY - paddingTop) + (prevHeight - endHeight) / 2;
+ dy += yOffset;
+ v.setTranslationY(dy);
+ root.getOverlay().add(v);
+
+ // Expanding has a delay while the wallpaper begins to fade out
+ // Collapsing is opposite of this so wallpaper will have the delay instead
+ int startDelay = isExpanding ? ANIMATE_START_DELAY : 0;
+
+ v.animate()
+ .setStartDelay(startDelay)
+ .translationY(0)
+ .setDuration(ANIMATE_DURATION)
+ .setInterpolator(
+ new DecelerateInterpolator(ANIMATE_INTERPOLATE_FACTOR))
+ .withEndAction(new Runnable() {
+ public void run() {
+ root.getOverlay().remove(v);
+ mPreviewContent.addView(v, 0);
+ }
+ });
+ v.postDelayed(new Runnable() {
+ public void run() {
+ if (isExpanding) {
+ v.animateExpand();
+ }
+ }
+ }, ANIMATE_DURATION / 2);
+ }
+ return true;
+ }
+ });
+ }
+
+ private void animateExtras(final boolean isExpanding) {
+ int[] pos = new int[2];
+ mAdditionalCards.getLocationInWindow(pos);
+ final ViewGroup parent = (ViewGroup) mAdditionalCards.getParent();
+ final ViewGroup root = (ViewGroup) getActivity().getWindow()
+ .getDecorView().findViewById(android.R.id.content);
+
+ // During a collapse we don't want the card to shrink so add it to the overlay now
+ // During an expand we want the card to expand so add it to the overlay post-layout
+ if (!isExpanding) {
+ root.getOverlay().add(mAdditionalCards);
+ }
+
+ // Expanding has a delay while the wallpaper begins to fade out
+ // Collapsing is opposite of this so wallpaper will have the delay instead
+ final int startDelay = isExpanding ? ANIMATE_START_DELAY : 0;
+ final ViewTreeObserver observer = mScrollContent.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ observer.removeOnPreDrawListener(this);
+
+ int translationY = 0;
+ if (isExpanding) {
+ root.getOverlay().add(mAdditionalCards);
+ } else {
+ translationY = getDistanceToMoveBelowScreen(mAdditionalCards);
+ }
+
+ int duration = isExpanding ? ANIMATE_DURATION + 100 : ANIMATE_DURATION;
+ mAdditionalCards.animate()
+ .setStartDelay(startDelay)
+ .translationY(translationY)
+ .setDuration(duration)
+ .setInterpolator(
+ new DecelerateInterpolator(ANIMATE_INTERPOLATE_FACTOR))
+ .withEndAction(new Runnable() {
+ public void run() {
+ if (!isExpanding) {
+ mAdditionalCards.setVisibility(View.INVISIBLE);
+ }
+ root.getOverlay().remove(mAdditionalCards);
+ parent.addView(mAdditionalCards);
+ }
+ });
+ return false;
+ }
+ });
+ }
+
+ private int getDistanceToMoveBelowScreen(View v) {
+ Display display = getActivity().getWindowManager().getDefaultDisplay();
+ Point p = new Point();
+ display.getSize(p);
+ int heightId = getResources()
+ .getIdentifier("navigation_bar_height", "dimen", "android");
+ int navbar_height = getResources().getDimensionPixelSize(heightId);
+ int[] pos = new int[2];
+ v.getLocationInWindow(pos);
+ return p.y + navbar_height - pos[1];
+ }
+
+ private void animateTitleCard(final boolean expand, final boolean applyTheme) {
+ final ViewGroup parent = (ViewGroup) mTitleCard.getParent();
+ // Get current location of the title card
+ int[] location = new int[2];
+ mTitleCard.getLocationOnScreen(location);
+ final int prevY = location[1];
+ final int position = parent.indexOfChild(mTitleCard);
+
+ final ViewTreeObserver observer = mScrollContent.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ observer.removeOnPreDrawListener(this);
+
+ final ViewGroup root = (ViewGroup) getActivity().getWindow()
+ .getDecorView().findViewById(android.R.id.content);
+
+ root.getOverlay().add(mTitleCard);
+
+ //Move title card back where it was before the relayout
+ float alpha = 1f;
+ if (expand) {
+ int[] endPos = new int[2];
+ mTitleCard.getLocationInWindow(endPos);
+ int endY = endPos[1];
+ mTitleCard.setTranslationY(prevY - endY);
+ alpha = 0;
+ } else {
+ }
+
+ // Fade the title card and move it out of the way
+ mTitleCard.animate()
+ .alpha(alpha)
+ .setDuration(ANIMATE_DURATION)
+ .withEndAction(new Runnable() {
+ public void run() {
+ root.getOverlay().remove(mTitleCard);
+ parent.addView(mTitleCard, position);
+ if (expand) {
+ mTitleCard.setVisibility(View.INVISIBLE);
+ } else {
+ mTitleCard.setVisibility(View.VISIBLE);
+ mClickableView.setVisibility(View.VISIBLE);
+ if (applyTheme) {
+ // The title card is the last animation when collapsing so
+ // we will handle applying the theme, if applicable, here
+ applyTheme();
+ }
+ }
+ }
+ });
+ return true;
+ }
+ });
+ }
+
+ private void animateWallpaperOut() {
+ final ViewGroup root = (ViewGroup) getActivity().getWindow()
+ .getDecorView().findViewById(android.R.id.content);
+
+ int[] location = new int[2];
+ mWallpaper.getLocationOnScreen(location);
+
+ final int prevY = location[1];
+
+ final ViewTreeObserver observer = mScrollContent.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ observer.removeOnPreDrawListener(this);
+ root.getOverlay().add(mWallpaper);
+
+ int[] location = new int[2];
+ mWallpaper.getLocationOnScreen(location);
+ final int newY = location[1];
+
+ mWallpaper.setTranslationY(prevY - newY);
+ mWallpaper.animate()
+ .alpha(0f)
+ .setDuration(300)
+ .withEndAction(new Runnable() {
+ public void run() {
+ root.getOverlay().remove(mWallpaper);
+ mShadowFrame.addView(mWallpaper, 0);
+ mWallpaper.setVisibility(View.GONE);
+ }
+ });
+ return true;
+
+ }
+ });
+ }
+
+ private void animateWallpaperIn() {
+ mWallpaper.setVisibility(View.VISIBLE);
+ mWallpaper.setTranslationY(0);
+ mWallpaper.animate()
+ .alpha(1f)
+ .setDuration(300);
+ }
+
+ protected String getAppliedFontPackageName() {
+ final Configuration config = getActivity().getResources().getConfiguration();
+ final ThemeConfig themeConfig = config != null ? config.themeConfig : null;
+ return themeConfig != null ? themeConfig.getFontPkgName() :
+ ThemeConfig.getSystemTheme().getFontPkgName();
+ }
+
+ protected ThemeManager getThemeManager() {
+ return ThemeManager.getInstance(getActivity());
+ }
+
+ private void freeMediaPlayers() {
+ for (MediaPlayer mp : mMediaPlayers.values()) {
+ if (mp != null) {
+ mp.stop();
+ mp.release();
+ }
+ }
+ mMediaPlayers.clear();
+ }
+
+ protected View.OnClickListener mPlayPauseClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ MediaPlayer mp = (MediaPlayer) v.getTag();
+ if (mp != null) {
+ if (mp.isPlaying()) {
+ ((ImageView) v).setImageResource(R.drawable.media_sound_preview);
+ mp.pause();
+ mp.seekTo(0);
+ } else {
+ stopMediaPlayers();
+ ((ImageView) v).setImageResource(R.drawable.media_sound_stop);
+ mp.start();
+ }
+ }
+ }
+ };
+
+ protected MediaPlayer.OnCompletionListener mPlayCompletionListener
+ = new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ for (ImageView v : mMediaPlayers.keySet()) {
+ if (mp == mMediaPlayers.get(v)) {
+ if (v != null) {
+ v.setImageResource(R.drawable.media_sound_preview);
+ }
+ }
+ }
+ }
+ };
+
+ private void stopMediaPlayers() {
+ for (ImageView v : mMediaPlayers.keySet()) {
+ if (v != null) {
+ v.setImageResource(R.drawable.media_sound_preview);
+ }
+ MediaPlayer mp = mMediaPlayers.get(v);
+ if (mp != null && mp.isPlaying()) {
+ mp.pause();
+ mp.seekTo(0);
+ }
+ }
+ }
+
+ protected void resetTheme() {
+ mSelectedComponentsMap.clear();
+ Bundle args = new Bundle();
+ args.putString(ARG_PACKAGE_NAME, mBaseThemePkgName);
+ args.putLong(ARG_COMPONENT_ID, DEFAULT_COMPONENT_ID);
+ getLoaderManager().restartLoader(LOADER_ID_ALL, args, this);
+ mThemeResetting = true;
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ String pkgName = mPkgName;
+ long componentId = DEFAULT_COMPONENT_ID;
+ if (args != null) {
+ pkgName = args.getString(ARG_PACKAGE_NAME);
+ componentId = args.getLong(ARG_COMPONENT_ID, DEFAULT_COMPONENT_ID);
+ }
+ return CursorLoaderHelper.themeFragmentCursorLoader(getActivity(), id, pkgName,
+ componentId);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
+ if (c.getCount() == 0) return;
+ mCurrentCursor = c;
+ mCurrentLoaderId = loader.getId();
+ c.moveToFirst();
+ boolean animate = !mApplyThemeOnPopulated;
+ switch (mCurrentLoaderId) {
+ case LOADER_ID_ALL:
+ if (mProcessingResources && !isThemeProcessing()) {
+ mProcessingResources = false;
+ hideProcessingOverlay();
+ }
+ loadLegacyThemeInfo(c);
+ populateSupportedComponents(c);
+ loadWallpaper(c, false);
+ loadStatusBar(c, false);
+ loadIcons(c, false);
+ loadNavBar(c, false);
+ loadTitle(c);
+ loadFont(c, false);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ animateContentIn();
+ }
+ });
+ if (mShowLockScreenSelectorAfterContentLoaded) {
+ getChooserActivity().expandContentAndAnimateLockScreenCardIn();
+ mShowLockScreenSelectorAfterContentLoaded = false;
+ }
+ break;
+ case LOADER_ID_STATUS_BAR:
+ loadStatusBar(c, animate);
+ break;
+ case LOADER_ID_FONT:
+ loadFont(c, animate);
+ break;
+ case LOADER_ID_ICONS:
+ loadIcons(c, animate);
+ break;
+ case LOADER_ID_WALLPAPER:
+ loadWallpaper(c, animate);
+ break;
+ case LOADER_ID_NAVIGATION_BAR:
+ loadNavBar(c, animate);
+ break;
+ case LOADER_ID_LIVE_LOCK_SCREEN:
+ case LOADER_ID_LOCKSCREEN:
+ loadLockScreen(c, animate);
+ break;
+ case LOADER_ID_STYLE:
+ loadStyle(c, animate);
+ break;
+ case LOADER_ID_BOOT_ANIMATION:
+ loadBootAnimation(c);
+ break;
+ case LOADER_ID_RINGTONE:
+ loadAudible(RingtoneManager.TYPE_RINGTONE, c, animate);
+ break;
+ case LOADER_ID_NOTIFICATION:
+ loadAudible(RingtoneManager.TYPE_NOTIFICATION, c, animate);
+ break;
+ case LOADER_ID_ALARM:
+ loadAudible(RingtoneManager.TYPE_ALARM, c, animate);
+ break;
+ }
+ if (mCurrentLoaderId != LOADER_ID_ALL) {
+ if (!componentsChanged()) {
+ getChooserActivity().hideSaveApplyButton();
+ } else if (!mApplyThemeOnPopulated) {
+ getChooserActivity().showSaveApplyButton();
+ }
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {}
+
+ private void loadAdditionalCards(Cursor c) {
+ for(int i=0; i < mAdditionalCards.getChildCount(); i++) {
+ View v = mAdditionalCards.getChildAt(i);
+ if (v instanceof ComponentCardView) {
+ String component = mCardIdsToComponentTypes.get(v.getId());
+ loadAdditionalCard(c, component, shouldShowComponentCard(component));
+ }
+ }
+ }
+
+ private void loadAdditionalCard(Cursor c, String component, boolean hasContent) {
+ if (MODIFIES_LOCKSCREEN.equals(component)) {
+ if (hasContent) {
+ loadLockScreen(c, false);
+ } else {
+ mLockScreenCard.clearWallpaper();
+ mLockScreenCard.setEmptyViewEnabled(true);
+ setAddComponentTitle(mLockScreenCard, getString(R.string.lockscreen_label));
+ }
+ } else if (MODIFIES_LAUNCHER.equals(component)) {
+ // this was already loaded so no need to do this again.
+ } else if (MODIFIES_OVERLAYS.equals(component)) {
+ if (hasContent) {
+ loadStyle(c, false);
+ } else {
+ mStyleCard.setEmptyViewEnabled(true);
+ setAddComponentTitle(mStyleCard,
+ getString(R.string.style_label));
+ }
+ } else if (MODIFIES_BOOT_ANIM.equals(component)) {
+ if (hasContent) {
+ loadBootAnimation(c);
+ } else {
+ mBootAnimationCard.setEmptyViewEnabled(true);
+ setAddComponentTitle(mBootAnimationCard,
+ getString(R.string.boot_animation_label));
+ }
+ } else if (MODIFIES_RINGTONES.equals(component)) {
+ if (hasContent) {
+ loadAudible(RingtoneManager.TYPE_RINGTONE, c, false);
+ } else {
+ mRingtoneCard.setEmptyViewEnabled(true);
+ setAddComponentTitle(mRingtoneCard,
+ getAudibleLabel(RingtoneManager.TYPE_RINGTONE));
+ }
+ } else if (MODIFIES_NOTIFICATIONS.equals(component)) {
+ if (hasContent) {
+ loadAudible(RingtoneManager.TYPE_NOTIFICATION, c, false);
+ } else {
+ mNotificationCard.setEmptyViewEnabled(true);
+ setAddComponentTitle(mNotificationCard,
+ getAudibleLabel(RingtoneManager.TYPE_NOTIFICATION));
+ }
+ } else if (MODIFIES_ALARMS.equals(component)) {
+ if (hasContent) {
+ loadAudible(RingtoneManager.TYPE_ALARM, c, false);
+ } else {
+ mAlarmCard.setEmptyViewEnabled(true);
+ setAddComponentTitle(mAlarmCard,
+ getAudibleLabel(RingtoneManager.TYPE_ALARM));
+ }
+ } else {
+ throw new IllegalArgumentException("Don't know how to load: " + component);
+ }
+ }
+
+ protected void populateSupportedComponents(Cursor c) {
+ List<String> components = ThemeUtils.getAllComponents();
+ for(String component : components) {
+ int pkgIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+ int modifiesCompIdx = c.getColumnIndex(component);
+
+ String pkg = c.getString(pkgIdx);
+ boolean supported = (modifiesCompIdx >= 0) && (c.getInt(modifiesCompIdx) == 1);
+ if (supported) {
+ mBaseThemeSupportedComponents.add(component);
+ mSelectedComponentsMap.put(component, pkg);
+ }
+ }
+
+ if (mApplyThemeOnPopulated) {
+ applyTheme();
+ }
+ }
+
+ /**
+ * Determines whether a card should be shown or not.
+ * UX Rules:
+ * 1) "My Theme" always shows all cards
+ * 2) Other themes only show what has been implemented in the theme
+ *
+ */
+ protected Boolean shouldShowComponentCard(String component) {
+ String pkg = mSelectedComponentsMap.get(component);
+ return pkg != null && pkg.equals(mPkgName);
+ }
+
+ protected void loadLegacyThemeInfo(Cursor c) {
+ int targetApiIdx = c.getColumnIndex(ThemesColumns.TARGET_API);
+ // If this is being called for a MyThemeFragment the index will be -1 so set to
+ // SYSTEM_TARGET_API so we don't display the tag. If the user applied a legacy theme
+ // then they should have already been warned.
+ int targetApi = targetApiIdx < 0 ? SYSTEM_TARGET_API : c.getInt(targetApiIdx);
+ mIsLegacyTheme = targetApi != SYSTEM_TARGET_API && targetApi <= Build.VERSION_CODES.KITKAT;
+ mThemeTagLayout.setLegacyTagEnabled(mIsLegacyTheme);
+ }
+
+ protected void loadTitle(Cursor c) {
+ int titleIdx = c.getColumnIndex(ThemesColumns.TITLE);
+ int authorIdx = c.getColumnIndex(ThemesColumns.AUTHOR);
+ mTitle.setText(c.getString(titleIdx));
+ mAuthor.setText(c.getString(authorIdx));
+ }
+
+ protected void loadWallpaper(Cursor c, boolean animate) {
+ mExternalWallpaperUri = null;
+ Drawable overlay = null;
+ if (animate) {
+ overlay = getOverlayDrawable(mWallpaperCard, true);
+ }
+ if (mWallpaperCard.isShowingEmptyView()) mWallpaperCard.setEmptyViewEnabled(false);
+
+ int pkgNameIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+ int wpIdx = c.getColumnIndex(PreviewColumns.WALLPAPER_PREVIEW);
+ int cmpntIdIdx = c.getColumnIndex(PreviewColumns.COMPONENT_ID);
+ final Resources res = getResources();
+ Bitmap bitmap = Utils.loadBitmapBlob(c, wpIdx);
+ if (bitmap != null) {
+ mWallpaper.setImageBitmap(bitmap);
+ mWallpaperCard.setWallpaper(new BitmapDrawable(res, bitmap));
+ String pkgName = c.getString(pkgNameIdx);
+ Long cmpntId = (cmpntIdIdx >= 0) ? c.getLong(cmpntIdIdx) : DEFAULT_COMPONENT_ID;
+ if (!mPkgName.equals(pkgName) || (mPkgName.equals(pkgName)
+ && mBaseThemeSupportedComponents.contains(MODIFIES_LAUNCHER))) {
+ mSelectedComponentsMap.put(MODIFIES_LAUNCHER, pkgName);
+ mSelectedWallpaperComponentId = cmpntId;
+ setCardTitle(mWallpaperCard, pkgName, getString(R.string.wallpaper_label));
+ }
+ } else {
+ // Set the wallpaper to "None"
+ mWallpaperCard.setWallpaper(null);
+ setCardTitle(mWallpaperCard, WALLPAPER_NONE, getString(R.string.wallpaper_label));
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_LAUNCHER, WALLPAPER_NONE);
+ }
+
+ if (animate) {
+ animateContentChange(R.id.wallpaper_card, mWallpaperCard, overlay);
+ }
+ }
+
+ protected void loadLockScreen(Cursor c, boolean animate) {
+ mExternalLockscreenUri = null;
+ Drawable overlay = null;
+ if (animate) {
+ overlay = getOverlayDrawable(mLockScreenCard, true);
+ }
+ if (mLockScreenCard.isShowingEmptyView()) mLockScreenCard.setEmptyViewEnabled(false);
+
+ int pkgNameIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+ int liveLockIndex = c.getColumnIndex(MODIFIES_LIVE_LOCK_SCREEN);
+ boolean isLiveLockScreen = liveLockIndex >= 0 && c.getInt(liveLockIndex) == 1;
+
+ int wpIdx = isLiveLockScreen
+ ? c.getColumnIndex(PreviewColumns.LIVE_LOCK_SCREEN_PREVIEW)
+ : c.getColumnIndex(PreviewColumns.LOCK_WALLPAPER_PREVIEW);
+ final Resources res = getResources();
+ Bitmap bitmap = Utils.loadBitmapBlob(c, wpIdx);
+ if (bitmap != null) {
+ mLockScreenCard.setWallpaper(new BitmapDrawable(res, bitmap));
+ String pkgName = c.getString(pkgNameIdx);
+ if (!mPkgName.equals(pkgName) || (mPkgName.equals(pkgName)
+ && (mBaseThemeSupportedComponents.contains(MODIFIES_LOCKSCREEN) ||
+ mBaseThemeSupportedComponents.contains(MODIFIES_LIVE_LOCK_SCREEN)))) {
+ if (isLiveLockScreen) {
+ mSelectedComponentsMap.put(MODIFIES_LIVE_LOCK_SCREEN, pkgName);
+ if (mCurrentTheme.containsKey(MODIFIES_LOCKSCREEN)) {
+ mSelectedComponentsMap.put(MODIFIES_LOCKSCREEN, LOCKSCREEN_NONE);
+ } else {
+ mSelectedComponentsMap.remove(MODIFIES_LOCKSCREEN);
+ }
+ setCardTitle(mLockScreenCard, pkgName,
+ getString(R.string.live_lock_screen_label));
+ } else {
+ mSelectedComponentsMap.put(MODIFIES_LOCKSCREEN, pkgName);
+ if (mCurrentTheme.containsKey(MODIFIES_LIVE_LOCK_SCREEN)) {
+ mSelectedComponentsMap.put(MODIFIES_LIVE_LOCK_SCREEN, LOCKSCREEN_NONE);
+ } else {
+ mSelectedComponentsMap.remove(MODIFIES_LIVE_LOCK_SCREEN);
+ }
+ setCardTitle(mLockScreenCard, pkgName, getString(R.string.lockscreen_label));
+ }
+ }
+ } else {
+ // Set the lockscreen wallpaper to "None"
+ mLockScreenCard.setWallpaper(null);
+ setCardTitle(mLockScreenCard, WALLPAPER_NONE, getString(R.string.lockscreen_label));
+ }
+
+ if (animate) {
+ animateContentChange(R.id.lockscreen_card, mLockScreenCard, overlay);
+ }
+ }
+
+ protected void loadStatusBar(Cursor c, boolean animate) {
+ int backgroundIdx = c.getColumnIndex(PreviewColumns.STATUSBAR_BACKGROUND);
+ int wifiIdx = c.getColumnIndex(PreviewColumns.STATUSBAR_WIFI_ICON);
+ int wifiMarginIdx = c.getColumnIndex(PreviewColumns.STATUSBAR_WIFI_COMBO_MARGIN_END);
+ int bluetoothIdx = c.getColumnIndex(PreviewColumns.STATUSBAR_BLUETOOTH_ICON);
+ int signalIdx = c.getColumnIndex(PreviewColumns.STATUSBAR_SIGNAL_ICON);
+ int batteryIdx = c.getColumnIndex(Utils.getBatteryIndex(mBatteryStyle));
+ int clockColorIdx = c.getColumnIndex(PreviewColumns.STATUSBAR_CLOCK_TEXT_COLOR);
+ int pkgNameIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+
+ Bitmap background = Utils.loadBitmapBlob(c, backgroundIdx);
+ Bitmap bluetoothIcon = Utils.loadBitmapBlob(c, bluetoothIdx);
+ Bitmap wifiIcon = Utils.loadBitmapBlob(c, wifiIdx);
+ Bitmap signalIcon = Utils.loadBitmapBlob(c, signalIdx);
+ Bitmap batteryIcon = Utils.loadBitmapBlob(c, batteryIdx);
+ int wifiMargin = wifiMarginIdx != -1 ? c.getInt(wifiMarginIdx) : DEFAULT_WIFI_MARGIN;
+ int clockTextColor = clockColorIdx != -1 ? c.getInt(clockColorIdx) : DEFAULT_CLOCK_COLOR;
+
+ Drawable overlay = null;
+ if (animate) {
+ overlay = getOverlayDrawable(mStatusBar, false);
+ }
+ if (mStatusBarCard.isShowingEmptyView()) mStatusBarCard.setEmptyViewEnabled(false);
+
+ mStatusBar.setBackground(new BitmapDrawable(getActivity().getResources(), background));
+ mBluetooth.setImageBitmap(bluetoothIcon);
+ mWifi.setImageBitmap(wifiIcon);
+ mSignal.setImageBitmap(signalIcon);
+ mBattery.setImageBitmap(batteryIcon);
+ mClock.setTextColor(clockTextColor);
+
+ ViewGroup.MarginLayoutParams params =
+ (ViewGroup.MarginLayoutParams) mWifi.getLayoutParams();
+ params.setMarginEnd(wifiMargin);
+ mWifi.setLayoutParams(params);
+
+ if (mBatteryStyle == 4) {
+ mBattery.setVisibility(View.GONE);
+ } else {
+ mBattery.setVisibility(View.VISIBLE);
+ }
+ mStatusBar.post(new Runnable() {
+ @Override
+ public void run() {
+ mStatusBar.invalidate();
+ }
+ });
+ if (pkgNameIdx > -1) {
+ String pkgName = c.getString(pkgNameIdx);
+ if (!mPkgName.equals(pkgName) || (mPkgName.equals(pkgName)
+ && mBaseThemeSupportedComponents.contains(MODIFIES_STATUS_BAR))) {
+ mSelectedComponentsMap.put(MODIFIES_STATUS_BAR, pkgName);
+ setCardTitle(mStatusBarCard, pkgName,
+ getString(R.string.statusbar_label));
+ }
+ }
+ if (animate) {
+ animateContentChange(R.id.status_bar_container, mStatusBar, overlay);
+ }
+ }
+
+ protected void loadIcons(Cursor c, boolean animate) {
+ if (mIconCard.isShowingEmptyView()) {
+ mIconCard.setEmptyViewEnabled(false);
+ }
+ int[] iconIdx = new int[3];
+ iconIdx[0] = c.getColumnIndex(PreviewColumns.ICON_PREVIEW_1);
+ iconIdx[1] = c.getColumnIndex(PreviewColumns.ICON_PREVIEW_2);
+ iconIdx[2] = c.getColumnIndex(PreviewColumns.ICON_PREVIEW_3);
+ int pkgNameIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+
+ // Set the icons. If the provider does not have an icon preview then
+ // fall back to the default icon set
+ IconPreviewHelper helper = new IconPreviewHelper(getActivity(), "");
+ ViewGroup iconContainer =
+ (ViewGroup) mIconCard.findViewById(R.id.icon_preview_container);
+ int numOfChildren = iconContainer.getChildCount();
+
+ List<ImageView> iconViews = new ArrayList<ImageView>(numOfChildren);
+ for(int i=0; i < numOfChildren; i++) {
+ final View view = iconContainer.getChildAt(i);
+ if (!(view instanceof ImageView)) continue;
+ iconViews.add((ImageView) view);
+ }
+
+ for(int i=0; i < iconViews.size() && i < iconIdx.length; i++) {
+ final ImageView v = iconViews.get(i);
+ Bitmap bitmap = Utils.loadBitmapBlob(c, iconIdx[i]);
+ Drawable oldIcon = v.getDrawable();
+ Drawable newIcon;
+ if (bitmap == null) {
+ ComponentName component = sIconComponents[i];
+ newIcon = helper.getDefaultIcon(component.getPackageName(),
+ component.getClassName());
+ } else {
+ newIcon = new BitmapDrawable(getResources(), bitmap);
+ }
+ if (animate) {
+ Drawable[] layers = new Drawable[2];
+ layers[0] = oldIcon instanceof IconTransitionDrawable ?
+ ((IconTransitionDrawable) oldIcon).getDrawable(1) : oldIcon;
+ layers[1] = newIcon;
+ final IconTransitionDrawable itd = new IconTransitionDrawable(layers);
+ v.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ itd.startTransition(ANIMATE_COMPONENT_CHANGE_DURATION);
+ v.setImageDrawable(itd);
+ }
+ }, ANIMATE_COMPONENT_ICON_DELAY * i);
+ } else {
+ v.setImageDrawable(newIcon);
+ }
+ }
+ if (pkgNameIdx > -1) {
+ String pkgName = c.getString(pkgNameIdx);
+ if (!mPkgName.equals(pkgName) || (mPkgName.equals(pkgName)
+ && mBaseThemeSupportedComponents.contains(MODIFIES_ICONS))) {
+ mSelectedComponentsMap.put(MODIFIES_ICONS, pkgName);
+ setCardTitle(mIconCard, pkgName,
+ getString(R.string.icon_label));
+ }
+ }
+ }
+
+ protected void loadNavBar(Cursor c, boolean animate) {
+ int backButtonIdx = c.getColumnIndex(PreviewColumns.NAVBAR_BACK_BUTTON);
+ int homeButtonIdx = c.getColumnIndex(PreviewColumns.NAVBAR_HOME_BUTTON);
+ int recentButtonIdx = c.getColumnIndex(PreviewColumns.NAVBAR_RECENT_BUTTON);
+ int backgroundIdx = c.getColumnIndex(PreviewColumns.NAVBAR_BACKGROUND);
+ if (backgroundIdx == -1) {
+ backgroundIdx = c.getColumnIndex(PreviewColumns.STATUSBAR_BACKGROUND);
+ }
+ int pkgNameIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+
+ Bitmap background = Utils.loadBitmapBlob(c, backgroundIdx);
+ Bitmap backButton = Utils.loadBitmapBlob(c, backButtonIdx);
+ Bitmap homeButton = Utils.loadBitmapBlob(c, homeButtonIdx);
+ Bitmap recentButton = Utils.loadBitmapBlob(c, recentButtonIdx);
+
+ Drawable overlay = null;
+ if (animate) {
+ overlay = getOverlayDrawable(mNavBar, false);
+ }
+ if (mNavBarCard.isShowingEmptyView()) mNavBarCard.setEmptyViewEnabled(false);
+
+ mNavBar.setBackground(new BitmapDrawable(getActivity().getResources(), background));
+ mBackButton.setImageBitmap(backButton);
+ mHomeButton.setImageBitmap(homeButton);
+ mRecentButton.setImageBitmap(recentButton);
+
+ if (pkgNameIdx > -1) {
+ String pkgName = c.getString(pkgNameIdx);
+ if (!mPkgName.equals(pkgName) || (mPkgName.equals(pkgName)
+ && mBaseThemeSupportedComponents.contains(MODIFIES_NAVIGATION_BAR))) {
+ mSelectedComponentsMap.put(MODIFIES_NAVIGATION_BAR, pkgName);
+ setCardTitle(mNavBarCard, pkgName, getString(R.string.navbar_label));
+ }
+ }
+ if (animate) {
+ animateContentChange(R.id.navigation_bar_container, mNavBar, overlay);
+ }
+ }
+
+ protected void loadFont(Cursor c, boolean animate) {
+ Drawable overlay = null;
+ if (animate) {
+ overlay = getOverlayDrawable(mFontPreview, true);
+ }
+ if (mFontCard.isShowingEmptyView()) mFontCard.setEmptyViewEnabled(false);
+
+ int pkgNameIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+ String pkgName = pkgNameIdx >= 0 ? c.getString(pkgNameIdx) : mPkgName;
+ TypefaceHelperCache cache = TypefaceHelperCache.getInstance();
+ ThemedTypefaceHelper helper = cache.getHelperForTheme(getActivity(), pkgName);
+ mTypefaceNormal = helper.getTypeface(Typeface.NORMAL);
+ mFontPreview.setTypeface(mTypefaceNormal);
+ if (!mPkgName.equals(pkgName) || (mPkgName.equals(pkgName)
+ && mBaseThemeSupportedComponents.contains(MODIFIES_FONTS))) {
+ mSelectedComponentsMap.put(MODIFIES_FONTS, pkgName);
+ setCardTitle(mFontCard, pkgName, getString(R.string.font_label));
+ }
+
+ if (animate) {
+ animateContentChange(R.id.font_preview_container, mFontPreview, overlay);
+ }
+ }
+
+ protected void loadStyle(Cursor c, boolean animate) {
+ Drawable overlay = null;
+ if (animate) {
+ overlay = getOverlayDrawable(mStylePreview, true);
+ }
+ if (mStyleCard.isShowingEmptyView()) {
+ mStyleCard.setEmptyViewEnabled(false);
+ }
+
+ int pkgNameIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+ int styleIdx = c.getColumnIndex(PreviewColumns.STYLE_PREVIEW);
+ mStylePreview.setImageBitmap(Utils.loadBitmapBlob(c, styleIdx));
+ if (pkgNameIdx > -1) {
+ String pkgName = c.getString(pkgNameIdx);
+ if (!mPkgName.equals(pkgName) || (mPkgName.equals(pkgName)
+ && mBaseThemeSupportedComponents.contains(MODIFIES_OVERLAYS))) {
+ mSelectedComponentsMap.put(MODIFIES_OVERLAYS, pkgName);
+ setCardTitle(mStyleCard, pkgName,
+ getString(R.string.style_label));
+ }
+ }
+ if (animate) {
+ animateContentChange(R.id.style_card, mStylePreview, overlay);
+ }
+ }
+
+ protected void loadBootAnimation(Cursor c) {
+ if (mBootAnimationCard.isShowingEmptyView()) {
+ mBootAnimationCard.setEmptyViewEnabled(false);
+ }
+ int pkgNameIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+ if (mBootAnimation != null) {
+ String pkgName;
+ if (pkgNameIdx > -1) {
+ pkgName = c.getString(pkgNameIdx);
+ if (!mPkgName.equals(pkgName) || (mPkgName.equals(pkgName)
+ && mBaseThemeSupportedComponents.contains(MODIFIES_BOOT_ANIM))) {
+ mSelectedComponentsMap.put(MODIFIES_BOOT_ANIM, pkgName);
+ setCardTitle(mBootAnimationCard, pkgName,
+ getString(R.string.boot_animation_label));
+ }
+ } else {
+ pkgName = mCurrentTheme.get(MODIFIES_BOOT_ANIM);
+ }
+ mBootAnimation.stop();
+ new AnimationLoader(getActivity(), pkgName, mBootAnimation).execute();
+ }
+ }
+
+ protected void loadAudible(int type, Cursor c, boolean animate) {
+ ComponentCardView audibleContainer = null;
+ ImageView playPause = null;
+ String component = null;
+ int parentResId = 0;
+ switch (type) {
+ case RingtoneManager.TYPE_RINGTONE:
+ audibleContainer = mRingtoneCard;
+ playPause = mRingtonePlayPause;
+ component = MODIFIES_RINGTONES;
+ parentResId = R.id.ringtone_preview_container;
+ break;
+ case RingtoneManager.TYPE_NOTIFICATION:
+ audibleContainer = mNotificationCard;
+ playPause = mNotificationPlayPause;
+ component = MODIFIES_NOTIFICATIONS;
+ parentResId = R.id.notification_preview_container;
+ break;
+ case RingtoneManager.TYPE_ALARM:
+ audibleContainer = mAlarmCard;
+ playPause = mAlarmPlayPause;
+ component = MODIFIES_ALARMS;
+ parentResId = R.id.alarm_preview_container;
+ break;
+ }
+ if (audibleContainer == null) return;
+
+ View content = audibleContainer.findViewById(R.id.content);
+ Drawable overlay = null;
+ if (animate) {
+ overlay = getOverlayDrawable(content, true);
+ }
+ if (audibleContainer.isShowingEmptyView()) {
+ audibleContainer.setEmptyViewEnabled(false);
+ }
+
+ int pkgNameIdx = c.getColumnIndex(ThemesColumns.PKG_NAME);
+ int titleIdx = c.getColumnIndex(ThemesColumns.TITLE);
+ if (playPause == null) {
+ playPause = (ImageView) audibleContainer.findViewById(R.id.play_pause);
+ }
+ TextView title = (TextView) audibleContainer.findViewById(R.id.audible_name);
+ MediaPlayer mp = mMediaPlayers.get(playPause);
+ if (mp == null) {
+ mp = new MediaPlayer();
+ }
+ String pkgName = c.getString(pkgNameIdx);
+ setCardTitle(audibleContainer, pkgName, getAudibleLabel(type));
+ AudibleLoadingThread thread = new AudibleLoadingThread(getActivity(), type, pkgName, mp);
+ title.setText(c.getString(titleIdx));
+ if (!mPkgName.equals(pkgName) || (mPkgName.equals(pkgName)
+ && mBaseThemeSupportedComponents.contains(component))) {
+ mSelectedComponentsMap.put(component, pkgName);
+ }
+
+ playPause.setVisibility(View.VISIBLE);
+ playPause.setTag(mp);
+ mMediaPlayers.put(playPause, mp);
+ playPause.setOnClickListener(mPlayPauseClickListener);
+ mp.setOnCompletionListener(mPlayCompletionListener);
+ if (animate) {
+ animateContentChange(parentResId, content, overlay);
+ }
+ thread.start();
+ }
+
+ protected Drawable getOverlayDrawable(View v, boolean requiresTransparency) {
+ if (!v.isDrawingCacheEnabled()) v.setDrawingCacheEnabled(true);
+ Bitmap cache = v.getDrawingCache(true).copy(
+ requiresTransparency ? Config.ARGB_8888 : Config.RGB_565, false);
+ Drawable d = cache != null ? new BitmapDrawable(getResources(), cache) : null;
+ v.destroyDrawingCache();
+
+ return d;
+ }
+
+ protected String getAudibleLabel(int type) {
+ switch (type) {
+ case RingtoneManager.TYPE_RINGTONE:
+ return getString(R.string.ringtone_label);
+ case RingtoneManager.TYPE_NOTIFICATION:
+ return getString(R.string.notification_label);
+ case RingtoneManager.TYPE_ALARM:
+ return getString(R.string.alarm_label);
+ }
+ return null;
+ }
+
+ protected void setCardTitle(ComponentCardView card, String pkgName, String title) {
+ TextView tv = (TextView) card.findViewById(R.id.label);
+ if (Utils.getDefaultThemePackageName(getActivity()).equals(pkgName)) {
+ tv.setText(getString(R.string.default_tag_text) + " " + title);
+ } else {
+ tv.setText(title);
+ }
+ }
+
+ protected void setAddComponentTitle(ComponentCardView card, String title) {
+ TextView tv = (TextView) card.findViewById(R.id.label);
+ tv.setText(getString(R.string.add_component_text) + " " + title);
+ }
+
+ public static ComponentName[] getIconComponents(Context context) {
+ if (sIconComponents == null || sIconComponents.length == 0) {
+ sIconComponents = new ComponentName[]{COMPONENT_DIALER, COMPONENT_MESSAGING,
+ COMPONENT_CAMERA, COMPONENT_BROWSER};
+
+ PackageManager pm = context.getPackageManager();
+
+ // if device does not have telephony replace dialer and mms
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ sIconComponents[0] = COMPONENT_CALENDAR;
+ sIconComponents[1] = COMPONENT_GALERY;
+ } else {
+ // decide on which dialer icon to use
+ try {
+ if (pm.getPackageInfo(DIALER_NEXT_PACKAGE, 0) != null) {
+ sIconComponents[0] = COMPONENT_DIALERNEXT;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // default to COMPONENT_DIALER
+ }
+ }
+
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+ sIconComponents[2] = COMPONENT_SETTINGS;
+ } else {
+ // decide on which camera icon to use
+ try {
+ if (pm.getPackageInfo(CAMERA_NEXT_PACKAGE, 0) != null) {
+ sIconComponents[2] = COMPONENT_CAMERANEXT;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // default to COMPONENT_CAMERA
+ }
+ }
+
+ }
+ return sIconComponents;
+ }
+
+ private void setupCardClickListeners(View parent) {
+ for (int i = 0; i < mCardIdsToComponentTypes.size(); i++) {
+ parent.findViewById(mCardIdsToComponentTypes.keyAt(i))
+ .setOnClickListener(mCardClickListener);
+ }
+ }
+
+ private View.OnClickListener mCardClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (isShowingConfirmCancelOverlay() || isShowingCustomizeResetLayout()) return;
+ if (mActiveCardId > 0) {
+ // need to fade the newly selected card in if another was currently selected.
+ ((ComponentCardView) v).animateCardFadeIn();
+ }
+ mActiveCardId = v.getId();
+ String component = mCardIdsToComponentTypes.get(mActiveCardId);
+ // Only pass on mSelectedWallpaperComponentId if dealing with mods_launcher
+ long selectedComponentId = (ThemesColumns.MODIFIES_LAUNCHER.equals(component)) ?
+ mSelectedWallpaperComponentId : DEFAULT_COMPONENT_ID;
+ String pkgName = mSelectedComponentsMap.get(component);
+ if (component.equals(MODIFIES_LOCKSCREEN) && TextUtils.isEmpty(pkgName)) {
+ String liveLockScreenPkg = mSelectedComponentsMap.get(MODIFIES_LIVE_LOCK_SCREEN);
+ if (liveLockScreenPkg != null) {
+ pkgName = liveLockScreenPkg;
+ }
+ }
+ getChooserActivity().showComponentSelector(component, pkgName, selectedComponentId, v);
+ fadeOutNonSelectedCards(mActiveCardId);
+ stopMediaPlayers();
+ }
+ };
+
+ private ConfirmCancelOverlay.OnOverlayDismissedListener mApplyCancelListener =
+ new ConfirmCancelOverlay.OnOverlayDismissedListener() {
+ @Override
+ public void onDismissed(boolean accepted) {
+ hideConfirmCancelOverlay(accepted);
+ }
+ };
+
+ private ConfirmCancelOverlay.OnOverlayDismissedListener mDeleteConfirmationListener =
+ new ConfirmCancelOverlay.OnOverlayDismissedListener() {
+ @Override
+ public void onDismissed(boolean accepted) {
+ if (accepted) uninstallTheme();
+ hideConfirmCancelOverlay();
+ }
+ };
+
+ private ConfirmCancelOverlay.OnOverlayDismissedListener mResetConfirmationListener =
+ new ConfirmCancelOverlay.OnOverlayDismissedListener() {
+ @Override
+ public void onDismissed(boolean accepted) {
+ if (accepted) resetTheme();
+ hideConfirmCancelOverlay();
+ }
+ };
+
+ private View.OnClickListener mCustomizeResetClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (v == mDismissButton) {
+ hideCustomizeResetLayout(CustomizeResetAction.Dismiss);
+ } else if (v == mResetButton) {
+ hideCustomizeResetLayout(CustomizeResetAction.Reset);
+ } else if (v == mCustomizeButton) {
+ hideCustomizeResetLayout(CustomizeResetAction.Customize);
+ }
+ }
+ };
+
+ protected void loadComponentFromPackage(String pkgName, String component, long componentId) {
+ Bundle args = new Bundle();
+ args.putString(ARG_PACKAGE_NAME, pkgName);
+ args.putLong(ARG_COMPONENT_ID, componentId);
+ int loaderId = LOADER_ID_INVALID;
+ if (MODIFIES_STATUS_BAR.equals(component)) {
+ loaderId = LOADER_ID_STATUS_BAR;
+ } else if (MODIFIES_FONTS.equals(component)) {
+ loaderId = LOADER_ID_FONT;
+ } else if (MODIFIES_ICONS.equals(component)) {
+ loaderId = LOADER_ID_ICONS;
+ } else if (MODIFIES_NAVIGATION_BAR.equals(component)) {
+ loaderId = LOADER_ID_NAVIGATION_BAR;
+ } else if (MODIFIES_LAUNCHER.equals(component)) {
+ if (pkgName != null) {
+ if (TextUtils.isEmpty(pkgName)) {
+ mWallpaperCard.setWallpaper(null);
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_LAUNCHER, WALLPAPER_NONE);
+ setCardTitle(mWallpaperCard, WALLPAPER_NONE,
+ getString(R.string.wallpaper_label));
+ getChooserActivity().showSaveApplyButton();
+ } else if (ComponentSelector.EXTERNAL_WALLPAPER.equals(pkgName)) {
+ // Check if we have READ_EXTERNAL_STORAGE permission and if not request it,
+ // otherwise let the user pick an image
+ if (getActivity().checkSelfPermission(
+ READ_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {
+ mAfterPermissionGrantedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ getChooserActivity().pickExternalWallpaper();
+ setCardTitle(mWallpaperCard, WALLPAPER_NONE,
+ getString(R.string.wallpaper_label));
+ }
+ };
+ requestPermissions(new String[]{READ_EXTERNAL_STORAGE},
+ PERMISSION_REQUEST);
+ } else {
+ getChooserActivity().pickExternalWallpaper();
+ setCardTitle(mWallpaperCard, WALLPAPER_NONE,
+ getString(R.string.wallpaper_label));
+ }
+ } else {
+ loaderId = LOADER_ID_WALLPAPER;
+ }
+ }
+ } else if (MODIFIES_LOCKSCREEN.equals(component)
+ || MODIFIES_LIVE_LOCK_SCREEN.equals(component)) {
+ if (pkgName != null && TextUtils.isEmpty(pkgName)) {
+ mLockScreenCard.setWallpaper(null);
+ mSelectedComponentsMap.put(ThemesColumns.MODIFIES_LOCKSCREEN, LOCKSCREEN_NONE);
+
+ if(mSelectedComponentsMap.containsKey(MODIFIES_LIVE_LOCK_SCREEN)) {
+ mSelectedComponentsMap.put(MODIFIES_LIVE_LOCK_SCREEN, LOCKSCREEN_NONE);
+ }
+ setCardTitle(mLockScreenCard, WALLPAPER_NONE,
+ getString(R.string.lockscreen_label));
+ if (mLockScreenCard.isShowingEmptyView()) {
+ mLockScreenCard.setEmptyViewEnabled(false);
+ }
+ getChooserActivity().showSaveApplyButton();
+ } else if (ComponentSelector.EXTERNAL_WALLPAPER.equals(pkgName)) {
+ // Check if we have READ_EXTERNAL_STORAGE permission and if not request it,
+ // otherwise let the user pick an image
+ if (getActivity().checkSelfPermission(
+ READ_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {
+ mAfterPermissionGrantedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ getChooserActivity().pickExternalLockscreen();
+ setCardTitle(mLockScreenCard, WALLPAPER_NONE,
+ getString(R.string.lockscreen_label));
+ }
+ };
+ requestPermissions(new String[] {READ_EXTERNAL_STORAGE},
+ PERMISSION_REQUEST);
+ } else {
+ getChooserActivity().pickExternalLockscreen();
+ setCardTitle(mLockScreenCard, WALLPAPER_NONE,
+ getString(R.string.lockscreen_label));
+ }
+ } else if (ComponentSelector.MOD_LOCK.equals(pkgName)) {
+ startLiveLockScreenSettings();
+ } else {
+ if (MODIFIES_LIVE_LOCK_SCREEN.equals(component)) {
+ loaderId = LOADER_ID_LIVE_LOCK_SCREEN;
+ } else {
+ loaderId = LOADER_ID_LOCKSCREEN;
+ }
+ }
+ } else if (MODIFIES_OVERLAYS.equals(component)) {
+ loaderId = LOADER_ID_STYLE;
+ } else if (MODIFIES_BOOT_ANIM.equals(component)) {
+ loaderId = LOADER_ID_BOOT_ANIMATION;
+ } else if (MODIFIES_RINGTONES.equals(component)) {
+ loaderId = LOADER_ID_RINGTONE;
+ } else if (MODIFIES_NOTIFICATIONS.equals(component)) {
+ loaderId = LOADER_ID_NOTIFICATION;
+ } else if (MODIFIES_ALARMS.equals(component)) {
+ loaderId = LOADER_ID_ALARM;
+ } else {
+ return;
+ }
+
+ if (loaderId != LOADER_ID_INVALID) {
+ getLoaderManager().restartLoader(loaderId, args, ThemeFragment.this);
+ }
+ }
+
+ private OnItemClickedListener mOnComponentItemClicked = new OnItemClickedListener() {
+ @Override
+ public void onItemClicked(String pkgName, long componentId, Bundle params) {
+ String component = mSelector.getComponentType();
+ if (MODIFIES_LOCKSCREEN.equals(component) && params != null) {
+ boolean isLiveLockView = params.getBoolean(
+ ComponentSelector.IS_LIVE_LOCK_SCREEN_VIEW,false);
+ if (isLiveLockView) {
+ //We got here because an live lock thubmnail view was clicked. We need to
+ //replace the component to load the proper data from the provider.
+ component = MODIFIES_LIVE_LOCK_SCREEN;
+ }
+ }
+ loadComponentFromPackage(pkgName, component, componentId);
+ }
+ };
+
+ private void fadeOutNonSelectedCards(int selectedCardId) {
+ for (int i = 0; i < mCardIdsToComponentTypes.size(); i++) {
+ if (mCardIdsToComponentTypes.keyAt(i) != selectedCardId) {
+ ComponentCardView card = (ComponentCardView) getView().findViewById(
+ mCardIdsToComponentTypes.keyAt(i));
+ if (card != null) card.animateCardFadeOut();
+ }
+ }
+ }
+
+ protected void animateContentChange(int parentId, View viewToAnimate, Drawable overlay) {
+ ((ComponentCardView) getView().findViewById(parentId))
+ .animateContentChange(viewToAnimate, overlay, ANIMATE_COMPONENT_CHANGE_DURATION);
+ }
+
+ private Runnable mApplyThemeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ final Context context = getActivity();
+ if (context != null) {
+ // Post this on mHandler so the client is added and removed from the same
+ // thread
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ final Map<String, String> componentsToApply = getComponentsToApply();
+ if (componentsToApply != null && componentsToApply.size() > 0) {
+ final Map<String, String> fullMap
+ = fillMissingComponentsWithDefault(componentsToApply);
+ ThemeManager tm = getThemeManager();
+ if (tm != null) {
+ try {
+ tm.addClient(ThemeFragment.this);
+ } catch (IllegalArgumentException e) {
+ /* ignore since this means we already have a listener added */
+ }
+ ThemeChangeRequest request =
+ getThemeChangeRequestForComponents(fullMap);
+ boolean value = request.getReqeustType().
+ equals(RequestType.USER_REQUEST_MIXNMATCH);
+
+ tm.requestThemeChange(request, !value);
+ }
+ mApplyThemeOnPopulated = false;
+ } else {
+ onFinish(true);
+ }
+ }
+ });
+ }
+ }
+ };
+
+ protected Map<String, String> fillMissingComponentsWithDefault(
+ Map<String, String> originalMap) {
+ HashMap newMap = new HashMap<String, String>();
+ newMap.putAll(originalMap);
+ Map<String, String> defaultMap = getEmptyComponentsMap();
+ for(Map.Entry<String, String> entry : defaultMap.entrySet()) {
+ String component = entry.getKey();
+ String defaultPkg = entry.getValue();
+ if (!newMap.containsKey(component)) {
+ newMap.put(component, defaultPkg);
+ }
+ }
+ return newMap;
+ }
+
+ protected Map<String, String> getEmptyComponentsMap() {
+ List<String> componentsList = ThemeUtils.getAllComponents();
+ Map<String, String> defaultMap = new HashMap<>(componentsList.size());
+ for (String component : componentsList) {
+ defaultMap.put(component, "");
+ }
+ return defaultMap;
+ }
+
+ /**
+ * This is the method that will be called when applying a theme and the idea is to override
+ * it in MyThemeFragment and pass in a different RequestType, once we have a type that indicates
+ * the user is mixing and matching instead of applying an entire theme.
+ * @param componentMap
+ * @return
+ */
+ protected ThemeChangeRequest getThemeChangeRequestForComponents(
+ Map<String, String> componentMap) {
+ return getThemeChangeRequestForComponents(componentMap, RequestType.USER_REQUEST);
+ }
+
+ protected ThemeChangeRequest getThemeChangeRequestForComponents(
+ Map<String, String> componentMap, RequestType requestType) {
+ ThemeChangeRequest.Builder builder = new ThemeChangeRequest.Builder();
+ for (String component : componentMap.keySet()) {
+ builder.setComponent(component, componentMap.get(component));
+ }
+ builder.setRequestType(requestType);
+ if (mThemeVersion >= 3) {
+ builder.setWallpaperId(mSelectedWallpaperComponentId != null
+ ? mSelectedWallpaperComponentId
+ : DEFAULT_COMPONENT_ID);
+ }
+ return builder.build();
+ }
+
+ protected Map<String, String> getComponentsToApply() {
+ return mSelectedComponentsMap;
+ }
+
+ 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);
+ }
+ }
+ }
+ };
+
+ class RestoreLockScreenCardRunnable implements Runnable {
+
+ final private boolean mWasShowingNone;
+ private String mCurrentLockScreenPkgName;
+
+ public RestoreLockScreenCardRunnable(boolean w, String pkgName) {
+ mWasShowingNone = w;
+ mCurrentLockScreenPkgName = pkgName;
+ }
+
+ @Override
+ public void run() {
+ if (!TextUtils.isEmpty(mCurrentLockScreenPkgName)) {
+ loadComponentFromPackage(mCurrentLockScreenPkgName,
+ MODIFIES_LOCKSCREEN, 0);
+ } else if (mWasShowingNone) {
+ mLockScreenCard.setWallpaper(null);
+ TextView none = (TextView) mLockScreenCard.findViewById(
+ R.id.none);
+ if (none != null) {
+ none.setVisibility(View.VISIBLE);
+ }
+ mLockScreenCard.setEmptyViewEnabled(false);
+ } else {
+ mLockScreenCard.clearWallpaper();
+ TextView none = (TextView) mLockScreenCard.findViewById(
+ R.id.none);
+ if (none != null) {
+ none.setVisibility(View.GONE);
+ }
+ mLockScreenCard.setEmptyViewEnabled(true);
+ setAddComponentTitle(mLockScreenCard, getString(R.string.lockscreen_label));
+ }
+ }
+ }
+
+ protected void applyTheme() {
+ if (mExternalWallpaperUri == null && mExternalLockscreenUri == null &&
+ (mSelectedComponentsMap == null || mSelectedComponentsMap.size() <= 0)) {
+ return;
+ }
+ final Map<String,String> componentsToApply = getComponentsToApply();
+ boolean isLLSEnabled = CMSettings.Secure.getInt(getActivity().getContentResolver(),
+ LIVE_LOCK_SCREEN_ENABLED, 0) == 1;
+ if (!TextUtils.isEmpty(componentsToApply.get(MODIFIES_LIVE_LOCK_SCREEN)) && !isLLSEnabled) {
+ AlertDialog d = new AlertDialog.Builder(getActivity(),
+ android.R.style.Theme_Material_Dialog)
+ .setTitle(R.string.enable_live_lock_screen_dialog_title)
+ .setMessage(R.string.enable_live_lock_screen_dialog_message)
+ .setPositiveButton(R.string.enable_live_lock_screen_dialog_positive_btn_text,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ CMSettings.Secure.putInt(getActivity().getContentResolver(),
+ LIVE_LOCK_SCREEN_ENABLED, 1);
+ getChooserActivity().themeChangeStart();
+ animateProgressIn(mApplyThemeRunnable);
+ }
+ })
+ .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mSelectedComponentsMap.remove(MODIFIES_LIVE_LOCK_SCREEN);
+ boolean wasNone = false;
+ if (TextUtils.equals("", mSelectedComponentsMap.get(
+ MODIFIES_LOCKSCREEN))) {
+ if (!TextUtils.isEmpty(mCurrentTheme.get(MODIFIES_LOCKSCREEN))) {
+ //The map entry was set to empty string because there's a
+ //lockscreen currently applied and setting this entry to empty
+ //would instruct the ThemeManager to clear the currently applied
+ //lockscreen, but the user decided not to enable LLS so we need
+ //to abort this change
+ mSelectedComponentsMap.put(MODIFIES_LOCKSCREEN,
+ mCurrentTheme.get(MODIFIES_LOCKSCREEN));
+ } else {
+ wasNone = true;
+ }
+ }
+ //Restore the lockscreen card to its previous state
+ mHandler.post(new RestoreLockScreenCardRunnable(wasNone,
+ mCurrentTheme.get(MODIFIES_LOCKSCREEN)));
+
+ //Did the user make more changes?
+ boolean modLLS = componentsToApply.containsKey(
+ MODIFIES_LIVE_LOCK_SCREEN);
+ boolean modLockscreen = componentsToApply.containsKey(
+ MODIFIES_LOCKSCREEN);
+ if (modLLS && ((modLockscreen && componentsToApply.size() > 2) ||
+ (!modLockscreen && componentsToApply.size() > 1))) {
+ getChooserActivity().themeChangeStart();
+ animateProgressIn(mApplyThemeRunnable);
+ }
+ }
+ })
+ .setCancelable(false)
+ .create();
+ d.setCanceledOnTouchOutside(false);
+ d.show();
+ } else {
+ getChooserActivity().themeChangeStart();
+ animateProgressIn(mApplyThemeRunnable);
+ }
+ }
+
+ /**
+ * Use when applyTheme() might be too early. ie mSelectedComponentsMap is not pop. yet
+ * @param pkgName Only used in MyThemeFragment to apply components on top of current theme
+ * @param components Optional list of components to apply.
+ */
+ protected void applyThemeWhenPopulated(String pkgName, List<String> components) {
+ mApplyThemeOnPopulated = true;
+ }
+
+ private void animateProgressIn(Runnable endAction) {
+ mProgress.setVisibility(View.VISIBLE);
+ mProgress.setProgress(0);
+ float pivotX = mTitleLayout.getWidth() -
+ getResources().getDimensionPixelSize(R.dimen.apply_progress_padding);
+ ScaleAnimation scaleAnim = new ScaleAnimation(0f, 1f, 1f, 1f,
+ pivotX, 0f);
+ scaleAnim.setDuration(ANIMATE_PROGRESS_IN_DURATION);
+
+ mTitleLayout.animate()
+ .translationXBy(-(pivotX / 3))
+ .alpha(0f)
+ .setDuration(ANIMATE_TITLE_OUT_DURATION)
+ .setInterpolator(new AccelerateInterpolator())
+ .withEndAction(endAction).start();
+ mProgress.startAnimation(scaleAnim);
+ }
+
+ private void animateProgressOut() {
+ mProgress.setVisibility(View.VISIBLE);
+ float pivotX = mTitleLayout.getWidth() -
+ getResources().getDimensionPixelSize(R.dimen.apply_progress_padding);
+ ScaleAnimation scaleAnim = new ScaleAnimation(1f, 0f, 1f, 1f,
+ pivotX, 0f);
+ scaleAnim.setDuration(ANIMATE_PROGRESS_OUT_DURATION);
+ scaleAnim.setFillAfter(false);
+ scaleAnim.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mProgress.setVisibility(View.GONE);
+ if (mThemeResetting) {
+ mThemeResetting = false;
+ mThemeTagLayout.setCustomizedTagEnabled(false);
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+
+ mTitleLayout.animate()
+ .translationXBy((pivotX / 3))
+ .alpha(1f)
+ .setDuration(ANIMATE_TITLE_IN_DURATION)
+ .setInterpolator(new AccelerateInterpolator())
+ .start();
+ mProgress.startAnimation(scaleAnim);
+ if (mThemeResetting) mReset.setVisibility(View.GONE);
+ }
+
+ private void animateContentIn() {
+ if (mSkipLoadingAnim) {
+ return;
+ }
+ AnimatorSet set = new AnimatorSet();
+ set.setDuration(ANIMATE_TITLE_IN_DURATION);
+ set.play(ObjectAnimator.ofFloat(mLoadingView, "alpha", 1f, 0f))
+ .with(ObjectAnimator.ofFloat(mTitleLayout, "alpha", 0f, 1f));
+ set.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLoadingView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ });
+ set.start();
+ }
+
+ private void disableActionButtons() {
+ mCustomize.setEnabled(false);
+ mDelete.setEnabled(false);
+ mReset.setEnabled(false);
+ }
+
+ private void enableActionButtons() {
+ mCustomize.setEnabled(true);
+ mDelete.setEnabled(true);
+ mReset.setEnabled(true);
+ }
+
+ public boolean isShowingConfirmCancelOverlay() {
+ return mConfirmCancelOverlay.getVisibility() == View.VISIBLE;
+ }
+
+ public void showApplyThemeOverlay() {
+ if (mConfirmCancelOverlay.getVisibility() == View.VISIBLE) return;
+ mConfirmCancelOverlay.setTitle(R.string.apply_theme_overlay_title);
+ mConfirmCancelOverlay.setBackgroundColor(getActivity().getResources()
+ .getColor(R.color.apply_overlay_background));
+ mConfirmCancelOverlay.setOnOverlayDismissedListener(mApplyCancelListener);
+ getChooserActivity().lockPager();
+ ViewPropertyAnimator anim = mConfirmCancelOverlay.animate();
+ mConfirmCancelOverlay.setVisibility(View.VISIBLE);
+ mConfirmCancelOverlay.setAlpha(0f);
+ anim.setListener(null);
+ anim.setDuration(ANIMATE_APPLY_LAYOUT_DURATION);
+ anim.alpha(1f).start();
+
+ if (mIsLegacyTheme) {
+ // Display cm11 theme warning message
+ TextView tv = (TextView) mConfirmCancelOverlay.findViewById(R.id.warning_message);
+ tv.setVisibility(View.VISIBLE);
+ tv.setText(String.format(getString(R.string.legacy_theme_warning), mTitle.getText()));
+ } else if (Utils.hasPerAppThemesApplied(getActivity())) {
+ // Display per app theme changes will be removed warning
+ TextView tv = (TextView) mConfirmCancelOverlay.findViewById(R.id.warning_message);
+ tv.setVisibility(View.VISIBLE);
+ tv.setText(String.format(getString(R.string.per_app_theme_removal_warning),
+ mTitle.getText()));
+ }
+
+ disableActionButtons();
+ mClickableView.setSoundEffectsEnabled(false);
+ }
+
+ public void showDeleteThemeOverlay() {
+ if (mConfirmCancelOverlay.getVisibility() == View.VISIBLE) return;
+ mConfirmCancelOverlay.setTitle(R.string.delete_theme_overlay_title);
+ mConfirmCancelOverlay.setBackgroundColor(getActivity().getResources()
+ .getColor(R.color.delete_overlay_background));
+ mConfirmCancelOverlay.setOnOverlayDismissedListener(mDeleteConfirmationListener);
+ getChooserActivity().lockPager();
+ ViewPropertyAnimator anim = mConfirmCancelOverlay.animate();
+ mConfirmCancelOverlay.setVisibility(View.VISIBLE);
+ mConfirmCancelOverlay.setAlpha(0f);
+ anim.setListener(null);
+ anim.setDuration(ANIMATE_APPLY_LAYOUT_DURATION);
+ anim.alpha(1f).start();
+
+ disableActionButtons();
+ mClickableView.setSoundEffectsEnabled(false);
+ }
+
+ public void showResetThemeOverlay() {
+ if (mConfirmCancelOverlay.getVisibility() == View.VISIBLE) return;
+ mConfirmCancelOverlay.setTitle(R.string.reset_theme_overlay_title);
+ mConfirmCancelOverlay.setBackgroundColor(getActivity().getResources()
+ .getColor(R.color.apply_overlay_background));
+ mConfirmCancelOverlay.setOnOverlayDismissedListener(mResetConfirmationListener);
+ getChooserActivity().lockPager();
+ ViewPropertyAnimator anim = mConfirmCancelOverlay.animate();
+ mConfirmCancelOverlay.setVisibility(View.VISIBLE);
+ mConfirmCancelOverlay.setAlpha(0f);
+ anim.setListener(null);
+ anim.setDuration(ANIMATE_APPLY_LAYOUT_DURATION);
+ anim.alpha(1f).start();
+
+ disableActionButtons();
+ mClickableView.setSoundEffectsEnabled(false);
+ }
+
+ public void hideConfirmCancelOverlay() {
+ hideConfirmCancelOverlay(false);
+ }
+
+ /**
+ * Hides the apply theme layout overlay and can apply the selected theme
+ * when the animation is finished.
+ * @param applyThemeWhenFinished If true, the current theme will be applied.
+ */
+ private void hideConfirmCancelOverlay(final boolean applyThemeWhenFinished) {
+ getChooserActivity().unlockPager();
+ ViewPropertyAnimator anim = mConfirmCancelOverlay.animate();
+ mConfirmCancelOverlay.setVisibility(View.VISIBLE);
+ anim.setDuration(ANIMATE_APPLY_LAYOUT_DURATION);
+ anim.alpha(0f).start();
+ anim.setListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mConfirmCancelOverlay.setVisibility(View.GONE);
+ if (applyThemeWhenFinished) applyTheme();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ });
+
+ enableActionButtons();
+ mClickableView.setSoundEffectsEnabled(true);
+ }
+
+ public boolean isShowingCustomizeResetLayout() {
+ return mCustomizeResetLayout.getVisibility() == View.VISIBLE;
+ }
+
+ public void showCustomizeResetLayout() {
+ if (mCustomizeResetLayout.getVisibility() == View.VISIBLE) return;
+ if (!mThemeTagLayout.isCustomizedTagEnabled()) {
+ mResetButton.setEnabled(false);
+ } else {
+ mResetButton.setEnabled(true);
+ }
+ getChooserActivity().lockPager();
+ ViewPropertyAnimator anim = mCustomizeResetLayout.animate();
+ mCustomizeResetLayout.setVisibility(View.VISIBLE);
+ mCustomizeResetLayout.setAlpha(0f);
+ anim.setListener(null);
+ anim.setDuration(ANIMATE_APPLY_LAYOUT_DURATION);
+ anim.alpha(1f).start();
+
+ disableActionButtons();
+ mClickableView.setSoundEffectsEnabled(false);
+ }
+
+ public void hideCustomizeResetLayout() {
+ hideCustomizeResetLayout(CustomizeResetAction.Dismiss);
+ }
+
+ private void hideCustomizeResetLayout(final CustomizeResetAction action) {
+ getChooserActivity().unlockPager();
+ ViewPropertyAnimator anim = mCustomizeResetLayout.animate();
+ mCustomizeResetLayout.setVisibility(View.VISIBLE);
+ anim.setDuration(ANIMATE_APPLY_LAYOUT_DURATION);
+ anim.alpha(0f).start();
+ anim.setListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCustomizeResetLayout.setVisibility(View.GONE);
+ switch (action) {
+ case Customize:
+ getChooserActivity().expand();
+ break;
+ case Reset:
+ resetTheme();
+ break;
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ });
+
+ enableActionButtons();
+ mClickableView.setSoundEffectsEnabled(true);
+ }
+
+ public void showThemeTagLayout() {
+ mThemeTagLayout.setVisibility(View.VISIBLE);
+ mThemeTagLayout.animate().alpha(1f).setStartDelay(ANIMATE_START_DELAY).start();
+ }
+
+ public void hideThemeTagLayout() {
+ mThemeTagLayout.setAlpha(0f);
+ mThemeTagLayout.setVisibility(View.GONE);
+ }
+
+ public void hideProcessingOverlay() {
+ mProcessingThemeLayout.animate().alpha(0).withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mProcessingThemeLayout.setVisibility(View.GONE);
+ }
+ }).setDuration(ANIMATE_APPLY_LAYOUT_DURATION).start();
+ mCustomize.setVisibility(View.VISIBLE);
+ mCustomize.setAlpha(0f);
+ mCustomize.animate().alpha(1f).setDuration(ANIMATE_APPLY_LAYOUT_DURATION).start();
+ if (mDelete.getVisibility() != View.GONE) {
+ mDelete.setVisibility(View.VISIBLE);
+ mDelete.setAlpha(0f);
+ mDelete.animate().alpha(1f).setDuration(ANIMATE_APPLY_LAYOUT_DURATION).start();
+ }
+
+ enableActionButtons();
+ mClickableView.setSoundEffectsEnabled(true);
+ }
+
+ public void fadeInCards() {
+ for (int i = 0; i < mCardIdsToComponentTypes.size(); i++) {
+ final int key = mCardIdsToComponentTypes.keyAt(i);
+ if (key != mActiveCardId) {
+ ComponentCardView card = (ComponentCardView) getView().findViewById(key);
+ if (card != null) card.animateCardFadeIn();
+ }
+ }
+ mActiveCardId = -1;
+ }
+
+ 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;
+ }
+ if (ThemesColumns.MODIFIES_LAUNCHER.equals(key) &&
+ mCurrentWallpaperComponentId.value != mSelectedWallpaperComponentId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean isThemeCustomized() {
+ final String themePkgName = getThemePackageName();
+ for (String key : mSelectedComponentsMap.keySet()) {
+ final String selectedPkgName = mSelectedComponentsMap.get(key);
+ if (!themePkgName.equals(selectedPkgName)) {
+ return true;
+ }
+ if (mBaseThemeSupportedComponents.size() > 0 &&
+ !mBaseThemeSupportedComponents.contains(key)) {
+ return true;
+ }
+ }
+ // finally check if we're missing anything from mBaseThemeSupportedComponents
+ for (String component : mBaseThemeSupportedComponents) {
+ if (!mSelectedComponentsMap.containsKey(component)) return true;
+ }
+ return false;
+ }
+
+ public void clearChanges() {
+ mSelectedComponentsMap.clear();
+ mExternalWallpaperUri = null;
+ mExternalLockscreenUri = null;
+ View none = mLockScreenCard.findViewById(R.id.none);
+ if (none != null && none.getVisibility() == View.VISIBLE) {
+ none.setVisibility(View.GONE);
+ }
+ TextView tv = (TextView) mLockScreenCard.findViewById(R.id.label);
+ if (tv != null) {
+ tv.setAlpha(1f);
+ tv.setBackgroundResource(R.drawable.wallpaper_label_bg);
+ }
+ getLoaderManager().restartLoader(LOADER_ID_ALL, null, ThemeFragment.this);
+ }
+
+ public String getThemePackageName() {
+ if (mPkgName == null) {
+ // check if the package name is defined in the arguments bundle
+ Bundle bundle = getArguments();
+ if (bundle != null) {
+ mPkgName = bundle.getString(ARG_PACKAGE_NAME);
+ }
+ }
+ return mPkgName;
+ }
+
+ private void uninstallTheme() {
+ getChooserActivity().uninstallTheme(mPkgName);
+ }
+
+ public void setCurrentTheme(Map<String, String> currentTheme,
+ MutableLong currentWallpaperComponentId) {
+ mCurrentTheme = currentTheme;
+ mCurrentWallpaperComponentId = currentWallpaperComponentId;
+ }
+
+ /**
+ * Slides the scrollview content up and adds a space view at the bottom
+ * of mAdditionalCards so all content can be visible above the selector.
+ *
+ * We are using a ValueAnimator here to scroll the content rather than calling
+ * mScrollView.smoothScrollBy() since the speed of that animation cannot be customized.
+ * @param yDelta
+ * @param selectorHeight
+ */
+ public void slideContentIntoView(final int yDelta, int selectorHeight) {
+ Space space = (Space) mAdditionalCards.findViewById(ADDITIONAL_CONTENT_SPACE_ID);
+ if (space == null) {
+ // No space view yet so lets create it one
+ space = new Space(getActivity());
+ space.setId(ADDITIONAL_CONTENT_SPACE_ID);
+ mAdditionalCards.addView(space,
+ new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
+ selectorHeight));
+ } else {
+ // Space view already exists so just update the LayoutParams
+ LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) space.getLayoutParams();
+ params.height = selectorHeight;
+ space.setLayoutParams(params);
+ }
+ final int startY = mScrollView.getScrollY();
+ final ValueAnimator scrollAnimator =
+ ValueAnimator.ofInt(startY, startY + yDelta);
+ scrollAnimator.setDuration(SLIDE_CONTENT_ANIM_DURATION);
+ scrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int value = (Integer) animation.getAnimatedValue();
+ mScrollView.scrollTo(0, value);
+ }
+ });
+ scrollAnimator.start();
+ }
+
+ public Map<String, String> getSelectedComponentsMap() {
+ return mSelectedComponentsMap;
+ }
+
+ /**
+ * Slides the scrollview content down and removes a space view at the bottom
+ * of mAdditionalCards.
+ *
+ * We are using a ValueAnimator here to scroll the content rather than calling
+ * mScrollView.smoothScrollBy() since the speed of that animation cannot be customized.
+ * @param yDelta
+ */
+ public void slideContentBack(int yDelta) {
+ final int startY = mScrollView.getScrollY();
+ final ValueAnimator scrollAnimator =
+ ValueAnimator.ofInt(startY, startY + yDelta);
+ scrollAnimator.setDuration(SLIDE_CONTENT_ANIM_DURATION);
+ scrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int value = (Integer) animation.getAnimatedValue();
+ mScrollView.scrollTo(0, value);
+ }
+ });
+ scrollAnimator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ View space = mAdditionalCards.findViewById(ADDITIONAL_CONTENT_SPACE_ID);
+ if (space != null) mAdditionalCards.removeView(space);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ });
+ scrollAnimator.start();
+ }
+
+ public void showLockScreenCard() {
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ final int scrollToY = mStatusBarCard.getMeasuredHeight()
+ + mFontCard.getMeasuredHeight() + mIconCard.getMeasuredHeight()
+ + mNavBarCard.getMeasuredHeight() + mWallpaperCard.getMeasuredHeight() / 2;
+ final ValueAnimator scrollAnimator = ValueAnimator.ofInt(0, scrollToY);
+ scrollAnimator.setDuration(LOCK_SCREEN_CARD_SCROLL_ANIMATION_DURATION);
+ scrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int value = (Integer) animation.getAnimatedValue();
+ mScrollView.scrollTo(0, value);
+ }
+ });
+ scrollAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mCardClickListener.onClick(mLockScreenCard);
+ }
+ });
+ scrollAnimator.start();
+ }
+ }, SHOW_LOCK_SCREEN_CARD_DELAY);
+ }
+
+ protected void startLiveLockScreenSettings() {
+ Intent intent = new Intent(cyanogenmod.content.Intent.ACTION_OPEN_LIVE_LOCKSCREEN_SETTINGS);
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ // TODO: inform user that this action failed (Toast?)
+ }
+ }
+
+ class AnimationLoader extends AsyncTask<Void, Void, Boolean> {
+ Context mContext;
+ String mPkgName;
+ BootAniImageView mBootAnim;
+
+ public AnimationLoader(Context context, String pkgName, BootAniImageView bootAnim) {
+ mContext = context;
+ mPkgName = pkgName;
+ mBootAnim = bootAnim;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ if (mContext == null) {
+ return Boolean.FALSE;
+ }
+ ZipFile zip = null;
+ if (ThemeConfig.SYSTEM_DEFAULT.equals(mPkgName)) {
+ try {
+ zip = new ZipFile(new File(BootAnimationHelper.SYSTEM_BOOT_ANI_PATH));
+ } catch (Exception e) {
+ Log.w(TAG, "Unable to load boot animation", e);
+ return Boolean.FALSE;
+ }
+ } else {
+ // check if the bootanimation is cached
+ File f = new File(mContext.getCacheDir(),
+ mPkgName + BootAnimationHelper.CACHED_SUFFIX);
+ if (!f.exists()) {
+ // go easy on cache storage and clear out any previous boot animations
+ BootAnimationHelper.clearBootAnimationCache(mContext);
+ try {
+ Context themeContext = mContext.createPackageContext(mPkgName, 0);
+ AssetManager am = themeContext.getAssets();
+ InputStream is = am.open("bootanimation/bootanimation.zip");
+ FileUtils.copyToFile(is, f);
+ is.close();
+ } catch (Exception e) {
+ Log.w(TAG, "Unable to load boot animation", e);
+ return Boolean.FALSE;
+ }
+ }
+ try {
+ zip = new ZipFile(f);
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to load boot animation", e);
+ return Boolean.FALSE;
+ }
+ }
+ if (zip != null) {
+ mBootAnim.setBootAnimation(zip);
+ } else {
+ return Boolean.FALSE;
+ }
+ return Boolean.TRUE;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean isSuccess) {
+ super.onPostExecute(isSuccess);
+ if (isSuccess) {
+ mBootAnim.start();
+ }
+ }
+ }
+
+ class AudibleLoadingThread extends Thread {
+ private Context mContext;
+ private int mType;
+ private String mPkgName;
+ private MediaPlayer mPlayer;
+
+ public AudibleLoadingThread(Context context, int type, String pkgName, MediaPlayer mp) {
+ super();
+ mContext = context;
+ mType = type;
+ mPkgName = pkgName;
+ mPlayer = mp;
+ }
+
+ @Override
+ public void run() {
+ try {
+ AudioUtils.loadThemeAudible(mContext, mType, mPkgName, mPlayer);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Unable to load sound for " + mPkgName, e);
+ }
+ }
+ }
+}
diff --git a/src/org/cyanogenmod/theme/chooser2/WallpaperCardView.java b/src/org/cyanogenmod/theme/chooser2/WallpaperCardView.java
new file mode 100644
index 0000000..74c39f5
--- /dev/null
+++ b/src/org/cyanogenmod/theme/chooser2/WallpaperCardView.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2016 Cyanogen, Inc.
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.theme.chooser2;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewOverlay;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class WallpaperCardView extends ComponentCardView {
+ protected ImageView mImage;
+ protected TextView mLabel;
+
+ public WallpaperCardView(Context context) {
+ this(context, null);
+ }
+
+ public WallpaperCardView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WallpaperCardView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WallpaperCardView);
+ String labelText = a.getString(R.styleable.WallpaperCardView_labelText);
+ a.recycle();
+
+ setOrientation(VERTICAL);
+
+ setBackgroundResource(R.drawable.card_bg);
+
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ FrameLayout frameLayout =
+ (FrameLayout) inflater.inflate(R.layout.wallpaper_card, this, false);
+ addView(frameLayout);
+ mLabel = (TextView) frameLayout.findViewById(R.id.label);
+ mImage = (ImageView) frameLayout.findViewById(R.id.image);
+
+ mLabel.setText(labelText);
+ }
+
+ public void setWallpaper(Drawable drawable) {
+ mImage.setImageDrawable(drawable);
+ View none = findViewById(R.id.none);
+ if (drawable == null) {
+ setBackgroundResource(R.drawable.card_wallpapertoggled_bg);
+ if (none != null) {
+ none.setVisibility(View.VISIBLE);
+ }
+ if (mLabel != null) {
+ mLabel.setBackgroundResource(0);
+ }
+ } else {
+ setBackgroundResource(0);
+
+ if (none != null) {
+ none.setVisibility(View.GONE);
+ }
+ if (mLabel != null) {
+ mLabel.setBackgroundResource(R.drawable.wallpaper_label_bg);
+ }
+ }
+ }
+
+ public void clearWallpaper() {
+ mImage.setImageDrawable(null);
+ setBackgroundResource(R.drawable.card_bg);
+ }
+
+ public Drawable getWallpaperDrawable() {
+ return mImage.getDrawable();
+ }
+
+ @Override
+ public void expand(boolean showLabel) {
+ setEnabled(true);
+ }
+
+ @Override
+ public void collapse() {
+ setEnabled(false);
+ }
+
+ /**
+ * Animates a change in the content of the card
+ * @param v View in card to animate
+ * @param overlay Drawable to animate as a ViewOverlay
+ * @param duration Duration of animation
+ */
+ @Override
+ public void animateContentChange(View v, final Drawable overlay, long duration) {
+ // Since the wallpaper IS the content, we will ignore the view passed in and animate
+ // the entire card
+ final ViewOverlay viewOverlay = this.getOverlay();
+ viewOverlay.add(overlay);
+ final int x = 0;
+ final int y = 0;
+ final int width = v.getWidth();
+ final int height = v.getHeight();
+ overlay.setBounds(x, y, x + width, y + height);
+
+ final ValueAnimator overlayAnimator = ValueAnimator.ofFloat(1f, 0f);
+ overlayAnimator.setDuration(duration);
+ overlayAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float value = (Float) animation.getAnimatedValue();
+ overlay.setAlpha((int) (255 * value));
+ }
+
+ });
+ overlayAnimator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Clear out the ViewOverlay now that we are done animating
+ viewOverlay.clear();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {}
+ });
+
+ AnimatorSet set = new AnimatorSet();
+ set.play(ObjectAnimator.ofFloat(overlay, "alpha", 0f, 1f))
+ .with(overlayAnimator);
+ set.start();
+ }
+}