From b8cdd6817c45ecd077496f036453012d403d68f5 Mon Sep 17 00:00:00 2001 From: Clark Scheff Date: Tue, 15 Jul 2014 15:38:31 -0700 Subject: Animate icon change in card when changing icons. Change-Id: Ibb4d825c3c1d53006b7f8f6df69ac1b00c95adda --- .../theme/chooserv2/IconTransitionDrawable.java | 171 +++++++++++++++++++++ .../cyanogenmod/theme/chooserv2/ThemeFragment.java | 39 +++-- 2 files changed, 197 insertions(+), 13 deletions(-) create mode 100644 src/org/cyanogenmod/theme/chooserv2/IconTransitionDrawable.java (limited to 'src/org') diff --git a/src/org/cyanogenmod/theme/chooserv2/IconTransitionDrawable.java b/src/org/cyanogenmod/theme/chooserv2/IconTransitionDrawable.java new file mode 100644 index 0000000..c0138a1 --- /dev/null +++ b/src/org/cyanogenmod/theme/chooserv2/IconTransitionDrawable.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2014 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.chooserv2; + +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.os.SystemClock; + +/** + * An extension of LayerDrawables that is intended to cross-fade between + * the first and second layer. To start the transition, call {@link #startTransition(int)}. To + * display just the first layer, call {@link #resetTransition()}. + *

+ * It can be defined in an XML file with the <transition> element. + * Each Drawable in the transition is defined in a nested <item>. For more + * information, see the guide to Drawable Resources.

+ * + * @attr ref android.R.styleable#LayerDrawableItem_left + * @attr ref android.R.styleable#LayerDrawableItem_top + * @attr ref android.R.styleable#LayerDrawableItem_right + * @attr ref android.R.styleable#LayerDrawableItem_bottom + * @attr ref android.R.styleable#LayerDrawableItem_drawable + * @attr ref android.R.styleable#LayerDrawableItem_id + * + */ +public class IconTransitionDrawable extends LayerDrawable { + + /** + * A transition is about to start. + */ + private static final int TRANSITION_STARTING = 0; + + /** + * The transition has started and the animation is in progress + */ + private static final int TRANSITION_RUNNING = 1; + + /** + * No transition will be applied + */ + private static final int TRANSITION_NONE = 2; + + /** + * The current state of the transition. One of {@link #TRANSITION_STARTING}, + * {@link #TRANSITION_RUNNING} and {@link #TRANSITION_NONE} + */ + private int mTransitionState = TRANSITION_NONE; + + private long mStartTimeMillis; + private int mFrom; + private int mTo; + private int mDuration; + private int mAlpha = 0; + private float mFromScale; + private float mToScale; + + /** + * Create a new transition drawable with the specified list of layers. At least + * 2 layers are required for this drawable to work properly. + */ + public IconTransitionDrawable(Drawable[] layers) { + super(layers); + } + + /** + * Begin the second layer on top of the first layer. + * + * @param durationMillis The length of the transition in milliseconds + */ + public void startTransition(int durationMillis) { + mFrom = 0; + mTo = 255; + mAlpha = 0; + mFromScale = 0f; + mToScale = 1.0f; + mDuration = durationMillis; + mTransitionState = TRANSITION_STARTING; + invalidateSelf(); + } + + /** + * Show only the first layer. + */ + public void resetTransition() { + mAlpha = 0; + mTransitionState = TRANSITION_NONE; + invalidateSelf(); + } + + @Override + public void draw(Canvas canvas) { + boolean done = true; + float scale = 0f; + + switch (mTransitionState) { + case TRANSITION_STARTING: + mStartTimeMillis = SystemClock.uptimeMillis(); + done = false; + mTransitionState = TRANSITION_RUNNING; + break; + + case TRANSITION_RUNNING: + if (mStartTimeMillis >= 0) { + float normalized = (float) + (SystemClock.uptimeMillis() - mStartTimeMillis) / mDuration; + done = normalized >= 1.0f; + normalized = Math.min(normalized, 1.0f); + mAlpha = (int) (mFrom + (mTo - mFrom) * normalized); + scale = mFromScale + (mToScale - mFromScale) * normalized; + } + break; + } + + final int alpha = mAlpha; + + if (done) { + // the setAlpha() calls below trigger invalidation and redraw. If we're done, just draw + // the appropriate drawable[s] and return + if (alpha == 0) { + getDrawable(0).draw(canvas); + } + if (alpha == 0xFF) { + getDrawable(1).draw(canvas); + + } + return; + } + + Drawable d; + d = getDrawable(0); + d.setAlpha(255 - alpha); + int cx = getIntrinsicWidth() / 2; + int cy = getIntrinsicHeight() / 2; + canvas.save(); + canvas.scale(1.0f - scale, 1.0f - scale, cx, cy); + d.draw(canvas); + canvas.restore(); + d.setAlpha(0xFF); + + if (alpha > 0) { + d = getDrawable(1); + d.setAlpha(alpha); + canvas.save(); + canvas.scale(scale, scale, cx, cy); + d.draw(canvas); + canvas.restore(); + d.setAlpha(0xFF); + } + + if (!done) { + invalidateSelf(); + } + } +} diff --git a/src/org/cyanogenmod/theme/chooserv2/ThemeFragment.java b/src/org/cyanogenmod/theme/chooserv2/ThemeFragment.java index 85fae45..09df7ea 100644 --- a/src/org/cyanogenmod/theme/chooserv2/ThemeFragment.java +++ b/src/org/cyanogenmod/theme/chooserv2/ThemeFragment.java @@ -66,12 +66,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static android.provider.ThemesContract.ThemesColumns.MODIFIES_ALARMS; -import static android.provider.ThemesContract.ThemesColumns.MODIFIES_BOOT_ANIM; -import static android.provider.ThemesContract.ThemesColumns.MODIFIES_LAUNCHER; -import static android.provider.ThemesContract.ThemesColumns.MODIFIES_NOTIFICATIONS; -import static android.provider.ThemesContract.ThemesColumns.MODIFIES_OVERLAYS; -import static android.provider.ThemesContract.ThemesColumns.MODIFIES_RINGTONES; import static android.provider.ThemesContract.ThemesColumns.MODIFIES_STATUS_BAR; import static android.provider.ThemesContract.ThemesColumns.MODIFIES_NAVIGATION_BAR; import static android.provider.ThemesContract.ThemesColumns.MODIFIES_ICONS; @@ -81,6 +75,8 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb 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 String CURRENTLY_APPLIED_THEME = "currently_applied_theme"; @@ -694,7 +690,7 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb case LOADER_ID_ALL: loadWallpaper(c); loadStatusBar(c); - loadIcons(c); + loadIcons(c, false); loadNavBar(c); loadTitle(c); loadFont(c); @@ -706,7 +702,7 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb loadFont(c); break; case LOADER_ID_ICONS: - loadIcons(c); + loadIcons(c, true); break; case LOADER_ID_WALLPAPER: loadWallpaper(c); @@ -787,7 +783,7 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb } } - private void loadIcons(Cursor c) { + private void loadIcons(Cursor c, boolean animate) { int[] iconIdx = new int[4]; iconIdx[0] = c.getColumnIndex(PreviewColumns.ICON_PREVIEW_1); iconIdx[1] = c.getColumnIndex(PreviewColumns.ICON_PREVIEW_2); @@ -800,15 +796,32 @@ public class ThemeFragment extends Fragment implements LoaderManager.LoaderCallb IconPreviewHelper helper = new IconPreviewHelper(getActivity(), ""); ViewGroup container = (ViewGroup) mIconContainer.findViewById(R.id.icon_preview_container); for(int i=0; i < container.getChildCount() && i < iconIdx.length; i++) { - ImageView v = (ImageView) ((ViewGroup)mIconContainer.getChildAt(1)).getChildAt(i); + final ImageView v = (ImageView) ((ViewGroup)mIconContainer.getChildAt(1)).getChildAt(i); Bitmap bitmap = Utils.loadBitmapBlob(c, iconIdx[i]); + Drawable oldIcon = v.getDrawable(); + Drawable newIcon; if (bitmap == null) { ComponentName component = sIconComponents[i]; - Drawable icon = helper.getDefaultIcon(component.getPackageName(), + newIcon = helper.getDefaultIcon(component.getPackageName(), component.getClassName()); - v.setImageDrawable(icon); } else { - v.setImageBitmap(bitmap); + 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) { -- cgit v1.1