diff options
author | Andy Mast <andy@cyngn.com> | 2014-06-20 18:03:50 -0700 |
---|---|---|
committer | Clark Scheff <clark@cyngn.com> | 2014-07-01 16:37:51 -0700 |
commit | df5c7c4e50911aeb64e2c50cfe8a93b704ead6f7 (patch) | |
tree | 9118ecfa2d54cdaf16e45bf99230371c72dba6a3 /src/org/cyanogenmod | |
parent | cf1535e968419a53016c449196de93b3eb4e1ad5 (diff) | |
download | packages_apps_ThemeChooser-df5c7c4e50911aeb64e2c50cfe8a93b704ead6f7.zip packages_apps_ThemeChooser-df5c7c4e50911aeb64e2c50cfe8a93b704ead6f7.tar.gz packages_apps_ThemeChooser-df5c7c4e50911aeb64e2c50cfe8a93b704ead6f7.tar.bz2 |
V2 Animations
Change-Id: Iea81643df7beea9a4f9129ed5d751f7011626943
Diffstat (limited to 'src/org/cyanogenmod')
4 files changed, 550 insertions, 0 deletions
diff --git a/src/org/cyanogenmod/theme/chooserv2/ChooserActivity.java b/src/org/cyanogenmod/theme/chooserv2/ChooserActivity.java new file mode 100644 index 0000000..740f923 --- /dev/null +++ b/src/org/cyanogenmod/theme/chooserv2/ChooserActivity.java @@ -0,0 +1,93 @@ +/* + * 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.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.PagerAdapter; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; +import android.widget.FrameLayout; + +import org.cyanogenmod.theme.chooser.R; + +public class ChooserActivity extends FragmentActivity { + + private PagerContainer mContainer; + private ThemeViewPager mPager; + private PagerAdapter mAdapter; + private boolean mExpanded = false; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.v2_activity_main); + + mContainer = (PagerContainer) findViewById(R.id.pager_container); + mPager = (ThemeViewPager) findViewById(R.id.viewpager); + + mAdapter = new MyAdapter(getSupportFragmentManager()); + mPager.setOnClickListener(mPagerClickListener); + mPager.setAdapter(mAdapter); + DisplayMetrics dm = getResources().getDisplayMetrics(); + int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, dm); + mPager.setPageMargin(margin); + mPager.setOffscreenPageLimit(3); + } + + private View.OnClickListener mPagerClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + final FrameLayout.LayoutParams layout = + (FrameLayout.LayoutParams) mPager.getLayoutParams(); + mExpanded = !mExpanded; + if (mExpanded) { + mContainer.expand(); + ThemeFragment f = (ThemeFragment) getSupportFragmentManager() + .findFragmentByTag(getFragmentTag(mPager.getCurrentItem())); + f.expand(); + } else { + mContainer.collapse(); + ThemeFragment f = (ThemeFragment) getSupportFragmentManager() + .findFragmentByTag(getFragmentTag(mPager.getCurrentItem())); + f.collapse(); + } + } + }; + + private String getFragmentTag(int pos){ + return "android:switcher:"+R.id.viewpager+":"+pos; + } + + public static class MyAdapter extends FragmentPagerAdapter { + public MyAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public int getCount() { + return 3; + } + + @Override + public Fragment getItem(int position) { + return ThemeFragment.newInstance(); + } + } +} diff --git a/src/org/cyanogenmod/theme/chooserv2/PagerContainer.java b/src/org/cyanogenmod/theme/chooserv2/PagerContainer.java new file mode 100644 index 0000000..d3c0f60 --- /dev/null +++ b/src/org/cyanogenmod/theme/chooserv2/PagerContainer.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2012 Wireless Designs, LLC + * Portions copyright (C) 2014, The CyanogenMod Project + * + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package org.cyanogenmod.theme.chooserv2; + +import android.content.Context; +import android.graphics.Point; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.FrameLayout; + +/** + * PagerContainer: A layout that displays a ViewPager with its children that are outside + * the typical pager bounds. + */ +public class PagerContainer extends FrameLayout implements ViewPager.OnPageChangeListener { + private static final int ANIMATE_OUT_DURATION = 300; + private static final int ANIMATE_OUT_INTERPOLATE_FACTOR = 1; + private static final int ANIMATE_IN_DURATION = 300; + private static final int ANIMATE_IN_INTERPOLATE_FACTOR = 2; + + private ThemeViewPager mPager; + private Point mCenter = new Point(); + private Point mInitialTouch = new Point(); + + boolean mNeedsRedraw = false; + + public PagerContainer(Context context) { + super(context); + init(); + } + + public PagerContainer(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public PagerContainer(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + //Disable clipping of children so non-selected pages are visible + setClipChildren(false); + + //Child clipping doesn't work with hardware acceleration in Android 3.x/4.x + //You need to set this value here if using hardware acceleration in an + // application targeted at these releases. + setLayerType(View.LAYER_TYPE_SOFTWARE, null); + } + + @Override + protected void onFinishInflate() { + try { + mPager = (ThemeViewPager) getChildAt(0); + mPager.setOnPageChangeListener(this); + } catch (Exception e) { + throw new IllegalStateException("The root child of PagerContainer must be a ViewPager"); + } + } + + public ThemeViewPager getViewPager() { + return mPager; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + mCenter.x = w / 2; + mCenter.y = h / 2; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + //We capture any touches not already handled by the ViewPager + // to implement scrolling from a touch outside the pager bounds. + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + mInitialTouch.x = (int)ev.getX(); + mInitialTouch.y = (int)ev.getY(); + default: + ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y - mInitialTouch.y); + break; + } + + return mPager.dispatchTouchEvent(ev); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + //Force the container to redraw on scrolling. + //Without this the outer pages render initially and then stay static + if (mNeedsRedraw) invalidate(); + } + + @Override + public void onPageSelected(int position) { } + + @Override + public void onPageScrollStateChanged(int state) { + mNeedsRedraw = (state != ThemeViewPager.SCROLL_STATE_IDLE); + } + + public void expand() { + mPager.setExpanded(true); + int current = mPager.getCurrentItem(); + + if (current != 0) { + View lchild = mPager.getChildAt(current - 1); + animateChildOut(lchild, lchild.getX() - getWidth()); + } + + if (current < mPager.getAdapter().getCount() - 1) { + View rchild = mPager.getChildAt(current + 1); + animateChildOut(rchild, rchild.getX() + getWidth()); + } + } + + public void collapse() { + mPager.setExpanded(false); + int current = mPager.getCurrentItem(); + + if (current != 0) { + View lchild = mPager.getChildAt(current - 1); + animateChildIn(lchild); + } + + if (current < mPager.getAdapter().getCount() - 1) { + View rchild = mPager.getChildAt(current + 1); + animateChildIn(rchild); + } + } + + private void animateChildOut(final View v, float endX) { + v.animate() + .translationX(endX) + .setDuration(ANIMATE_OUT_DURATION) + .setInterpolator(new AccelerateInterpolator(ANIMATE_OUT_INTERPOLATE_FACTOR)); + } + + private void animateChildIn(final View v) { + v.animate() + .translationX(0) + .setDuration(ANIMATE_IN_DURATION) + .setInterpolator(new DecelerateInterpolator(ANIMATE_IN_INTERPOLATE_FACTOR)); + } +}
\ No newline at end of file diff --git a/src/org/cyanogenmod/theme/chooserv2/ThemeFragment.java b/src/org/cyanogenmod/theme/chooserv2/ThemeFragment.java new file mode 100644 index 0000000..7c4540c --- /dev/null +++ b/src/org/cyanogenmod/theme/chooserv2/ThemeFragment.java @@ -0,0 +1,204 @@ +/* + * 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.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.animation.DecelerateInterpolator; +import android.widget.AbsListView; +import android.widget.BaseAdapter; +import android.widget.ListAdapter; +import android.widget.ListView; + +import org.cyanogenmod.theme.chooser.R; + +import java.util.ArrayList; +import java.util.List; + +public class ThemeFragment extends Fragment { + public static final int ANIMATE_START_DELAY = 75; + public static final int ANIMATE_DURATION = 500; + public static final int ANIMATE_INTERPOLATE_FACTOR = 3; + private ListView mListView; + private ListAdapter mListAdapter; + + static ThemeFragment newInstance() { + ThemeFragment f = new ThemeFragment(); + return f; + } + + /** + * When creating, retrieve this instance's number from its arguments. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + /** + * The Fragment's UI is just a simple text view showing its + * instance number. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + ViewPager.LayoutParams params = new ViewPager.LayoutParams(); + params.width = ViewPager.LayoutParams.MATCH_PARENT; + params.height = ViewPager.LayoutParams.MATCH_PARENT; + + View v = inflater.inflate(R.layout.v2_fragment_pager_list, container, false); + v.setLayoutParams(params); + + mListView = (ListView) v.findViewById(android.R.id.list); + + return v; + } + + public void expand() { + ((ThemePreviewAdapter) mListAdapter).setExpanded(true); + ((ThemePreviewAdapter) mListAdapter).notifyDataSetChanged(); + animateChildren(); + } + + + public void collapse() { + ((ThemePreviewAdapter) mListAdapter).setExpanded(false); + ((ThemePreviewAdapter) mListAdapter).notifyDataSetChanged(); + animateChildren(); + } + + // This will animate the children's vertical value between the existing and + // new layout changes + private void animateChildren() { + // Animate each child in the listview + final List<Float> prevYs = new ArrayList<Float>(); + for (int i = 0; i < mListView.getChildCount(); i++) { + final View v = mListView.getChildAt(i); + prevYs.add(v.getY()); + + final ViewTreeObserver observer = mListView.getViewTreeObserver(); + observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + public boolean onPreDraw() { + observer.removeOnPreDrawListener(this); + for (int i = 0; i < mListView.getChildCount(); i++) { + View v = mListView.getChildAt(i); + float prevY = prevYs.get(i); + final float endY = v.getY(); + v.setTranslationY(prevY - endY); + v.animate() + .setStartDelay(ANIMATE_START_DELAY) + .translationY(0) + .setDuration(ANIMATE_DURATION) + .setInterpolator( + new DecelerateInterpolator(ANIMATE_INTERPOLATE_FACTOR)); + } + return false; + } + }); + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mListAdapter = new ThemePreviewAdapter(getActivity(), null); + mListView.setAdapter(mListAdapter); + } + + public static class ThemePreviewAdapter extends BaseAdapter { + public static final String PLACEHOLDER_TAG = "placeholder"; + private static final int PLACEHOLDER_POSITION = 0; + + int[] layouts = {0, + R.layout.v2item_statusbar, + R.layout.v2item_font, + R.layout.v2item_icon, + R.layout.v2item_navbar}; + + private Context mContext; + private LayoutInflater mInflater; + private boolean mIsExpanded; + private List<String> mList; + + public ThemePreviewAdapter(Context context, List<String> list) { + mContext = context; + mInflater = LayoutInflater.from(context); + mList = list; + } + + public void setExpanded(boolean isExpanded) { + mIsExpanded = isExpanded; + } + + @Override + public int getCount() { + return layouts.length; + } + + @Override + public Object getItem(int position) { + return mList.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + // This is just prototype code, needs to actually use convertView and + // view holder patterns + View view; + if (position == PLACEHOLDER_POSITION) { + view = new View(mContext); + view.setTag(PLACEHOLDER_TAG); + + int height = 0; + if (mIsExpanded) { + //TODO: There is probably a better way than hardcoding a height value + //Maybe seperate expanded/collapsed layouts for our child views, + //Or pass the expand mode through onMeasure. + height = 200; + } + + ListView.LayoutParams layout = + new ListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height); + view.setLayoutParams(layout); + } else { + view = mInflater.inflate(layouts[position], null, false); + if (mIsExpanded) { + AbsListView.LayoutParams layout = + (AbsListView.LayoutParams) view.getLayoutParams(); + if (layout == null) { + layout = new AbsListView.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, 400); + } + view.setLayoutParams(layout); + } + } + + return view; + } + } +} diff --git a/src/org/cyanogenmod/theme/chooserv2/ThemeViewPager.java b/src/org/cyanogenmod/theme/chooserv2/ThemeViewPager.java new file mode 100644 index 0000000..f7b27e2 --- /dev/null +++ b/src/org/cyanogenmod/theme/chooserv2/ThemeViewPager.java @@ -0,0 +1,81 @@ +/* + * 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.content.Context; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +public class ThemeViewPager extends ViewPager { + private boolean mExpanded; + + private boolean mIsDragging = false; + private float mSlop; + private float mLastX; + private float mLastY; + + public ThemeViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + initView(context); + } + + private void initView(Context context) { + mSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + } + + public void setExpanded(boolean expanded) { + mExpanded = expanded; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return true; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + mLastX = ev.getX(); + mLastY = ev.getY(); + break; + case MotionEvent.ACTION_MOVE: + float xDist = Math.abs(mLastX - ev.getX()); + float yDist = Math.abs(mLastY - ev.getY()); + if (xDist > mSlop || yDist > mSlop) { + mIsDragging = true; + } + break; + case MotionEvent.ACTION_CANCEL: + mIsDragging = false; + break; + case MotionEvent.ACTION_UP: + if (!mIsDragging) { + performClick(); + } + mIsDragging = false; + break; + } + + if (mExpanded) { + return true; + } + + return super.onTouchEvent(ev); + } +} |