diff options
Diffstat (limited to 'src/org/cyanogenmod/theme/chooser/ThemeFragment.java')
-rw-r--r-- | src/org/cyanogenmod/theme/chooser/ThemeFragment.java | 3059 |
1 files changed, 3059 insertions, 0 deletions
diff --git a/src/org/cyanogenmod/theme/chooser/ThemeFragment.java b/src/org/cyanogenmod/theme/chooser/ThemeFragment.java new file mode 100644 index 0000000..d0891a7 --- /dev/null +++ b/src/org/cyanogenmod/theme/chooser/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.chooser; + +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.chooser.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.chooser.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); + } + } + } +} |