20 | * Call this whenever the background of a translucent Activity has changed 21 | * to become opaque. Doing so will allow the {@link android.view.Surface} of 22 | * the Activity behind to be released. 23 | *
24 | * This call has no effect on non-translucent activities or on activities 25 | * with the {@link android.R.attr#windowIsFloating} attribute. 26 | */ 27 | public static void convertActivityFromTranslucent(Activity activity) { 28 | /*try { 29 | Method method = Activity.class.getDeclaredMethod("convertFromTranslucent"); 30 | method.setAccessible(true); 31 | method.invoke(activity); 32 | } catch (Throwable t) { 33 | }*/ 34 | } 35 | 36 | /** 37 | * Convert a translucent themed Activity 38 | * {@link android.R.attr#windowIsTranslucent} back from opaque to 39 | * translucent following a call to 40 | * {@link #convertActivityFromTranslucent(android.app.Activity)} . 41 | *
42 | * Calling this allows the Activity behind this one to be seen again. Once 43 | * all such Activities have been redrawn 44 | *
45 | * This call has no effect on non-translucent activities or on activities
46 | * with the {@link android.R.attr#windowIsFloating} attribute.
47 | */
48 | public static void convertActivityToTranslucent(Activity activity) {
49 | /*try {
50 | Class>[] classes = Activity.class.getDeclaredClasses();
51 | Class> translucentConversionListenerClazz = null;
52 | for (Class clazz : classes) {
53 | if (clazz.getSimpleName().contains("TranslucentConversionListener")) {
54 | translucentConversionListenerClazz = clazz;
55 | }
56 | }
57 | Method method = Activity.class.getDeclaredMethod("convertToTranslucent",
58 | translucentConversionListenerClazz);
59 | method.setAccessible(true);
60 | method.invoke(activity, new Object[] {
61 | null
62 | });
63 | } catch (Throwable t) {
64 | }*/
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/libraries/SwipeBackLayout/src/main/java/me/imid/swipebacklayout/lib/app/SwipeBackActivity.java:
--------------------------------------------------------------------------------
1 |
2 | package me.imid.swipebacklayout.lib.app;
3 |
4 | import android.app.Activity;
5 | import android.os.Bundle;
6 | import android.view.View;
7 |
8 | import me.imid.swipebacklayout.lib.SwipeBackLayout;
9 | import me.imid.swipebacklayout.lib.Utils;
10 |
11 | public class SwipeBackActivity extends Activity implements SwipeBackActivityBase {
12 | private SwipeBackActivityHelper mHelper;
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | mHelper = new SwipeBackActivityHelper(this);
18 | mHelper.onActivityCreate();
19 | }
20 |
21 | @Override
22 | protected void onPostCreate(Bundle savedInstanceState) {
23 | super.onPostCreate(savedInstanceState);
24 | mHelper.onPostCreate();
25 | }
26 |
27 | @Override
28 | public View findViewById(int id) {
29 | View v = super.findViewById(id);
30 | if (v == null && mHelper != null)
31 | return mHelper.findViewById(id);
32 | return v;
33 | }
34 |
35 | @Override
36 | public SwipeBackLayout getSwipeBackLayout() {
37 | return mHelper.getSwipeBackLayout();
38 | }
39 |
40 | @Override
41 | public void setSwipeBackEnable(boolean enable) {
42 | getSwipeBackLayout().setEnableGesture(enable);
43 | }
44 |
45 | @Override
46 | public void scrollToFinishActivity() {
47 | Utils.convertActivityToTranslucent(this);
48 | getSwipeBackLayout().scrollToFinishActivity();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/libraries/SwipeBackLayout/src/main/java/me/imid/swipebacklayout/lib/app/SwipeBackActivityBase.java:
--------------------------------------------------------------------------------
1 | package me.imid.swipebacklayout.lib.app;
2 |
3 | import me.imid.swipebacklayout.lib.SwipeBackLayout;
4 | /**
5 | * @author Yrom
6 | */
7 | public interface SwipeBackActivityBase {
8 | /**
9 | * @return the SwipeBackLayout associated with this activity.
10 | */
11 | public abstract SwipeBackLayout getSwipeBackLayout();
12 |
13 | public abstract void setSwipeBackEnable(boolean enable);
14 |
15 | /**
16 | * Scroll out contentView and finish the activity
17 | */
18 | public abstract void scrollToFinishActivity();
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/libraries/SwipeBackLayout/src/main/java/me/imid/swipebacklayout/lib/app/SwipeBackActivityHelper.java:
--------------------------------------------------------------------------------
1 | package me.imid.swipebacklayout.lib.app;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Color;
6 | import android.graphics.drawable.ColorDrawable;
7 | import android.view.View;
8 |
9 | import me.imid.swipebacklayout.lib.SwipeBackLayout;
10 | import me.imid.swipebacklayout.lib.Utils;
11 |
12 | /**
13 | * @author Yrom
14 | * @author PeterCxy
15 | */
16 | public class SwipeBackActivityHelper {
17 | protected Activity mActivity;
18 |
19 | private SwipeBackLayout mSwipeBackLayout;
20 |
21 | public SwipeBackActivityHelper(Activity activity) {
22 | mActivity = activity;
23 | }
24 |
25 | @SuppressWarnings("deprecation")
26 | public void onActivityCreate() {
27 | mSwipeBackLayout = new SwipeBackLayout(mActivity, getGlobalContext());
28 | }
29 |
30 | public void onPostCreate() {
31 | mSwipeBackLayout.attachToActivity(mActivity);
32 | }
33 |
34 | public View findViewById(int id) {
35 | if (mSwipeBackLayout != null) {
36 | return mSwipeBackLayout.findViewById(id);
37 | }
38 | return null;
39 | }
40 |
41 | public SwipeBackLayout getSwipeBackLayout() {
42 | return mSwipeBackLayout;
43 | }
44 |
45 | protected Context getGlobalContext() {
46 | return mActivity;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/libraries/SwipeBackLayout/src/main/java/me/imid/swipebacklayout/lib/app/SwipeBackPreferenceActivity.java:
--------------------------------------------------------------------------------
1 |
2 | package me.imid.swipebacklayout.lib.app;
3 |
4 | import me.imid.swipebacklayout.lib.SwipeBackLayout;
5 | import android.os.Bundle;
6 | import android.preference.PreferenceActivity;
7 | import android.view.View;
8 |
9 | public class SwipeBackPreferenceActivity extends PreferenceActivity implements SwipeBackActivityBase {
10 | private SwipeBackActivityHelper mHelper;
11 |
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | mHelper = new SwipeBackActivityHelper(this);
16 | mHelper.onActivityCreate();
17 | }
18 |
19 | @Override
20 | protected void onPostCreate(Bundle savedInstanceState) {
21 | super.onPostCreate(savedInstanceState);
22 | mHelper.onPostCreate();
23 | }
24 |
25 | @Override
26 | public View findViewById(int id) {
27 | View v = super.findViewById(id);
28 | if (v == null && mHelper != null)
29 | return mHelper.findViewById(id);
30 | return v;
31 | }
32 |
33 | @Override
34 | public SwipeBackLayout getSwipeBackLayout() {
35 | return mHelper.getSwipeBackLayout();
36 | }
37 | @Override
38 | public void setSwipeBackEnable(boolean enable) {
39 | getSwipeBackLayout().setEnableGesture(enable);
40 | }
41 |
42 | @Override
43 | public void scrollToFinishActivity() {
44 | getSwipeBackLayout().scrollToFinishActivity();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/libraries/SwipeBackLayout/src/main/res/drawable-xhdpi/shadow_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaperAirplane-Dev-Team/SwipeBack/62e17c4515b1018f07fdbd4a392751d73c67ed51/libraries/SwipeBackLayout/src/main/res/drawable-xhdpi/shadow_bottom.png
--------------------------------------------------------------------------------
/libraries/SwipeBackLayout/src/main/res/drawable-xhdpi/shadow_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaperAirplane-Dev-Team/SwipeBack/62e17c4515b1018f07fdbd4a392751d73c67ed51/libraries/SwipeBackLayout/src/main/res/drawable-xhdpi/shadow_left.png
--------------------------------------------------------------------------------
/libraries/SwipeBackLayout/src/main/res/drawable-xhdpi/shadow_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaperAirplane-Dev-Team/SwipeBack/62e17c4515b1018f07fdbd4a392751d73c67ed51/libraries/SwipeBackLayout/src/main/res/drawable-xhdpi/shadow_right.png
--------------------------------------------------------------------------------
/libraries/SwipeBackLayout/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
42 | * I've used this to be able to acomodate the TextView 43 | * and the {@link org.adw.library.widgets.discreteseekbar.internal.drawable.MarkerDrawable} 44 | * with the required positions and offsets 45 | *
46 | * 47 | * @hide 48 | */ 49 | public class Marker extends ViewGroup implements MarkerDrawable.MarkerAnimationListener { 50 | private static final int PADDING_DP = 4; 51 | private static final int ELEVATION_DP = 8; 52 | private static final int SEPARATION_DP = 30; 53 | //The TextView to show the info 54 | private TextView mNumber; 55 | //The max width of this View 56 | private int mWidth; 57 | //some distance between the thumb and our bubble marker. 58 | //This will be added to our measured height 59 | private int mSeparation; 60 | MarkerDrawable mMarkerDrawable; 61 | 62 | public Marker(Context context) { 63 | this(context, null); 64 | } 65 | 66 | public Marker(Context context, AttributeSet attrs) { 67 | this(context, attrs, R.attr.discreteSeekBarStyle); 68 | } 69 | 70 | public Marker(Context context, AttributeSet attrs, int defStyleAttr) { 71 | this(context, attrs, defStyleAttr, "0"); 72 | } 73 | 74 | public Marker(Context context, AttributeSet attrs, int defStyleAttr, String maxValue) { 75 | super(context, attrs, defStyleAttr); 76 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 77 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiscreteSeekBar, 78 | R.attr.discreteSeekBarStyle, R.style.DefaultSeekBar); 79 | 80 | int padding = (int) (PADDING_DP * displayMetrics.density) * 2; 81 | int textAppearanceId = a.getResourceId(R.styleable.DiscreteSeekBar_dsb_indicatorTextAppearance, 82 | R.style.DefaultIndicatorTextAppearance); 83 | mNumber = new TextView(context); 84 | //Add some padding to this textView so the bubble has some space to breath 85 | mNumber.setPadding(padding, 0, padding, 0); 86 | mNumber.setTextAppearance(context, textAppearanceId); 87 | mNumber.setGravity(Gravity.CENTER); 88 | mNumber.setText(maxValue); 89 | mNumber.setMaxLines(1); 90 | mNumber.setSingleLine(true); 91 | SeekBarCompat.setTextDirection(mNumber, TEXT_DIRECTION_LOCALE); 92 | mNumber.setVisibility(View.INVISIBLE); 93 | 94 | //add some padding for the elevation shadow not to be clipped 95 | //I'm sure there are better ways of doing this... 96 | setPadding(padding, padding, padding, padding); 97 | 98 | resetSizes(maxValue); 99 | 100 | mSeparation = (int) (SEPARATION_DP * displayMetrics.density); 101 | int thumbSize = (int) (ThumbDrawable.DEFAULT_SIZE_DP * displayMetrics.density); 102 | ColorStateList color = a.getColorStateList(R.styleable.DiscreteSeekBar_dsb_indicatorColor); 103 | mMarkerDrawable = new MarkerDrawable(color, thumbSize); 104 | mMarkerDrawable.setCallback(this); 105 | mMarkerDrawable.setMarkerListener(this); 106 | mMarkerDrawable.setExternalOffset(padding); 107 | 108 | //Elevation for anroid 5+ 109 | float elevation = a.getDimension(R.styleable.DiscreteSeekBar_dsb_indicatorElevation, ELEVATION_DP * displayMetrics.density); 110 | ViewCompat.setElevation(this, elevation); 111 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 112 | SeekBarCompat.setOutlineProvider(this, mMarkerDrawable); 113 | } 114 | a.recycle(); 115 | } 116 | 117 | public void resetSizes(String maxValue) { 118 | DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); 119 | //Account for negative numbers... is there any proper way of getting the biggest string between our range???? 120 | mNumber.setText("-" + maxValue); 121 | //Do a first forced measure call for the TextView (with the biggest text content), 122 | //to calculate the max width and use always the same. 123 | //this avoids the TextView from shrinking and growing when the text content changes 124 | int wSpec = MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels, MeasureSpec.AT_MOST); 125 | int hSpec = MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels, MeasureSpec.AT_MOST); 126 | mNumber.measure(wSpec, hSpec); 127 | mWidth = Math.max(mNumber.getMeasuredWidth(), mNumber.getMeasuredHeight()); 128 | removeView(mNumber); 129 | addView(mNumber, new FrameLayout.LayoutParams(mWidth, mWidth, Gravity.LEFT | Gravity.TOP)); 130 | } 131 | 132 | @Override 133 | protected void dispatchDraw(Canvas canvas) { 134 | mMarkerDrawable.draw(canvas); 135 | super.dispatchDraw(canvas); 136 | } 137 | 138 | @Override 139 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 140 | measureChildren(widthMeasureSpec, heightMeasureSpec); 141 | int widthSize = mWidth + getPaddingLeft() + getPaddingRight(); 142 | int heightSize = mWidth + getPaddingTop() + getPaddingBottom(); 143 | //This diff is the basic calculation of the difference between 144 | //a square side size and its diagonal 145 | //this helps us account for the visual offset created by MarkerDrawable 146 | //when leaving one of the corners un-rounded 147 | int diff = (int) ((1.41f * mWidth) - mWidth) / 2; 148 | setMeasuredDimension(widthSize, heightSize + diff + mSeparation); 149 | } 150 | 151 | @Override 152 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 153 | int left = getPaddingLeft(); 154 | int top = getPaddingTop(); 155 | int right = getWidth() - getPaddingRight(); 156 | int bottom = getHeight() - getPaddingBottom(); 157 | //the TetView is always layout at the top 158 | mNumber.layout(left, top, left + mWidth, top + mWidth); 159 | //the MarkerDrawable uses the whole view, it will adapt itself... 160 | // or it seems so... 161 | mMarkerDrawable.setBounds(left, top, right, bottom); 162 | } 163 | 164 | @Override 165 | protected boolean verifyDrawable(Drawable who) { 166 | return who == mMarkerDrawable || super.verifyDrawable(who); 167 | } 168 | 169 | @Override 170 | protected void onAttachedToWindow() { 171 | super.onAttachedToWindow(); 172 | //HACK: Sometimes, the animateOpen() call is made before the View is attached 173 | //so the drawable cannot schedule itself to run the animation 174 | //I think we can call it here safely. 175 | //I've seen it happen in android 2.3.7 176 | animateOpen(); 177 | } 178 | 179 | public void setValue(CharSequence value) { 180 | mNumber.setText(value); 181 | } 182 | 183 | public CharSequence getValue() { 184 | return mNumber.getText(); 185 | } 186 | 187 | public void animateOpen() { 188 | mMarkerDrawable.stop(); 189 | mMarkerDrawable.animateToPressed(); 190 | } 191 | 192 | public void animateClose() { 193 | mMarkerDrawable.stop(); 194 | mNumber.setVisibility(View.INVISIBLE); 195 | mMarkerDrawable.animateToNormal(); 196 | } 197 | 198 | @Override 199 | public void onOpeningComplete() { 200 | mNumber.setVisibility(View.VISIBLE); 201 | if (getParent() instanceof MarkerDrawable.MarkerAnimationListener) { 202 | ((MarkerDrawable.MarkerAnimationListener) getParent()).onOpeningComplete(); 203 | } 204 | } 205 | 206 | @Override 207 | public void onClosingComplete() { 208 | if (getParent() instanceof MarkerDrawable.MarkerAnimationListener) { 209 | ((MarkerDrawable.MarkerAnimationListener) getParent()).onClosingComplete(); 210 | } 211 | } 212 | 213 | @Override 214 | protected void onDetachedFromWindow() { 215 | super.onDetachedFromWindow(); 216 | mMarkerDrawable.stop(); 217 | } 218 | 219 | public void setColors(int startColor, int endColor) { 220 | mMarkerDrawable.setColors(startColor, endColor); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/java/org/adw/library/widgets/discreteseekbar/internal/PopupIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Gustavo Claramunt (AnderWeb) 2014. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.adw.library.widgets.discreteseekbar.internal; 18 | 19 | import android.content.Context; 20 | import android.graphics.PixelFormat; 21 | import android.graphics.Point; 22 | import android.graphics.Rect; 23 | import android.os.Build; 24 | import android.os.IBinder; 25 | import android.support.v4.view.GravityCompat; 26 | import android.util.AttributeSet; 27 | import android.util.DisplayMetrics; 28 | import android.view.Gravity; 29 | import android.view.View; 30 | import android.view.ViewGroup; 31 | import android.view.WindowManager; 32 | import android.widget.FrameLayout; 33 | 34 | import org.adw.library.widgets.discreteseekbar.internal.compat.SeekBarCompat; 35 | import org.adw.library.widgets.discreteseekbar.internal.drawable.MarkerDrawable; 36 | 37 | /** 38 | * Class to manage the floating bubble thing, similar (but quite worse tested than {@link android.widget.PopupWindow} 39 | * 40 | *41 | * This will attach a View to the Window (full-width, measured-height, positioned just under the thumb) 42 | *
43 | * 44 | * @hide 45 | * @see #showIndicator(android.view.View, android.graphics.Rect) 46 | * @see #dismiss() 47 | * @see #dismissComplete() 48 | * @see org.adw.library.widgets.discreteseekbar.internal.PopupIndicator.Floater 49 | */ 50 | public class PopupIndicator { 51 | 52 | private final WindowManager mWindowManager; 53 | private boolean mShowing; 54 | private Floater mPopupView; 55 | //Outside listener for the DiscreteSeekBar to get MarkerDrawable animation events. 56 | //The whole chain of events goes this way: 57 | //MarkerDrawable->Marker->Floater->mListener->DiscreteSeekBar.... 58 | //... phew! 59 | private MarkerDrawable.MarkerAnimationListener mListener; 60 | private int[] mDrawingLocation = new int[2]; 61 | Point screenSize = new Point(); 62 | 63 | public PopupIndicator(Context context, AttributeSet attrs, int defStyleAttr, String maxValue) { 64 | mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 65 | mPopupView = new Floater(context, attrs, defStyleAttr, maxValue); 66 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 67 | screenSize.set(displayMetrics.widthPixels, displayMetrics.heightPixels); 68 | } 69 | 70 | public void updateSizes(String maxValue) { 71 | dismissComplete(); 72 | if (mPopupView != null) { 73 | mPopupView.mMarker.resetSizes(maxValue); 74 | } 75 | } 76 | 77 | public void setListener(MarkerDrawable.MarkerAnimationListener listener) { 78 | mListener = listener; 79 | } 80 | 81 | /** 82 | * We want the Floater to be full-width because the contents will be moved from side to side. 83 | * We may/should change this in the future to use just the PARENT View width and/or pass it in the constructor 84 | */ 85 | private void measureFloater() { 86 | int specWidth = View.MeasureSpec.makeMeasureSpec(screenSize.x, View.MeasureSpec.EXACTLY); 87 | int specHeight = View.MeasureSpec.makeMeasureSpec(screenSize.y, View.MeasureSpec.AT_MOST); 88 | mPopupView.measure(specWidth, specHeight); 89 | } 90 | 91 | public void setValue(CharSequence value) { 92 | mPopupView.mMarker.setValue(value); 93 | } 94 | 95 | public boolean isShowing() { 96 | return mShowing; 97 | } 98 | 99 | public void showIndicator(View parent, Rect touchBounds) { 100 | if (isShowing()) { 101 | mPopupView.mMarker.animateOpen(); 102 | return; 103 | } 104 | 105 | IBinder windowToken = parent.getWindowToken(); 106 | if (windowToken != null) { 107 | WindowManager.LayoutParams p = createPopupLayout(windowToken); 108 | 109 | p.gravity = Gravity.TOP | GravityCompat.START; 110 | updateLayoutParamsForPosiion(parent, p, touchBounds.bottom); 111 | mShowing = true; 112 | 113 | translateViewIntoPosition(touchBounds.centerX()); 114 | invokePopup(p); 115 | } 116 | } 117 | 118 | public void move(int x) { 119 | if (!isShowing()) { 120 | return; 121 | } 122 | translateViewIntoPosition(x); 123 | } 124 | 125 | public void setColors(int startColor, int endColor) { 126 | mPopupView.setColors(startColor, endColor); 127 | } 128 | 129 | /** 130 | * This will start the closing animation of the Marker and call onClosingComplete when finished 131 | */ 132 | public void dismiss() { 133 | mPopupView.mMarker.animateClose(); 134 | } 135 | 136 | /** 137 | * FORCE the popup window to be removed. 138 | * You typically calls this when the parent view is being removed from the window to avoid a Window Leak 139 | */ 140 | public void dismissComplete() { 141 | if (isShowing()) { 142 | mShowing = false; 143 | try { 144 | mWindowManager.removeViewImmediate(mPopupView); 145 | } finally { 146 | } 147 | } 148 | } 149 | 150 | private void updateLayoutParamsForPosiion(View anchor, WindowManager.LayoutParams p, int yOffset) { 151 | measureFloater(); 152 | int measuredHeight = mPopupView.getMeasuredHeight(); 153 | int paddingBottom = mPopupView.mMarker.getPaddingBottom(); 154 | anchor.getLocationInWindow(mDrawingLocation); 155 | p.x = 0; 156 | p.y = mDrawingLocation[1] - measuredHeight + yOffset + paddingBottom; 157 | p.width = screenSize.x; 158 | p.height = measuredHeight; 159 | } 160 | 161 | private void translateViewIntoPosition(final int x) { 162 | mPopupView.setFloatOffset(x + mDrawingLocation[0]); 163 | } 164 | 165 | private void invokePopup(WindowManager.LayoutParams p) { 166 | mWindowManager.addView(mPopupView, p); 167 | mPopupView.mMarker.animateOpen(); 168 | } 169 | 170 | private WindowManager.LayoutParams createPopupLayout(IBinder token) { 171 | WindowManager.LayoutParams p = new WindowManager.LayoutParams(); 172 | p.gravity = Gravity.START | Gravity.TOP; 173 | p.width = ViewGroup.LayoutParams.MATCH_PARENT; 174 | p.height = ViewGroup.LayoutParams.MATCH_PARENT; 175 | p.format = PixelFormat.TRANSLUCENT; 176 | p.flags = computeFlags(p.flags); 177 | p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; 178 | p.token = token; 179 | p.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN; 180 | p.setTitle("DiscreteSeekBar Indicator:" + Integer.toHexString(hashCode())); 181 | 182 | return p; 183 | } 184 | 185 | /** 186 | * I'm NOT completely sure how all this bitwise things work... 187 | * 188 | * @param curFlags 189 | * @return 190 | */ 191 | private int computeFlags(int curFlags) { 192 | curFlags &= ~( 193 | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES | 194 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | 195 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | 196 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | 197 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | 198 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); 199 | curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; 200 | curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 201 | curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 202 | curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; 203 | return curFlags; 204 | } 205 | 206 | /** 207 | * Small FrameLayout class to hold and move the bubble around when requested 208 | * I wanted to use the {@link Marker} directly 209 | * but doing so would make some things harder to implement 210 | * (like moving the marker around, having the Marker's outline to work, etc) 211 | */ 212 | private class Floater extends FrameLayout implements MarkerDrawable.MarkerAnimationListener { 213 | private Marker mMarker; 214 | private int mOffset; 215 | 216 | public Floater(Context context, AttributeSet attrs, int defStyleAttr, String maxValue) { 217 | super(context); 218 | mMarker = new Marker(context, attrs, defStyleAttr, maxValue); 219 | addView(mMarker, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP)); 220 | } 221 | 222 | @Override 223 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 224 | measureChildren(widthMeasureSpec, heightMeasureSpec); 225 | int widthSize = MeasureSpec.getSize(widthMeasureSpec); 226 | int heightSie = mMarker.getMeasuredHeight(); 227 | setMeasuredDimension(widthSize, heightSie); 228 | } 229 | 230 | @Override 231 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 232 | int centerDiffX = mMarker.getMeasuredWidth() / 2; 233 | int offset = (mOffset - centerDiffX); 234 | mMarker.layout(offset, 0, offset + mMarker.getMeasuredWidth(), mMarker.getMeasuredHeight()); 235 | } 236 | 237 | public void setFloatOffset(int x) { 238 | mOffset = x; 239 | int centerDiffX = mMarker.getMeasuredWidth() / 2; 240 | int offset = (x - centerDiffX); 241 | mMarker.offsetLeftAndRight(offset - mMarker.getLeft()); 242 | //Without hardware acceleration (or API levels<11), offsetting a view seems to NOT invalidate the proper area. 243 | //We should calc the proper invalidate Rect but this will be for now... 244 | if (!SeekBarCompat.isHardwareAccelerated(this)) { 245 | invalidate(); 246 | } 247 | } 248 | 249 | @Override 250 | public void onClosingComplete() { 251 | if (mListener != null) { 252 | mListener.onClosingComplete(); 253 | } 254 | dismissComplete(); 255 | } 256 | 257 | @Override 258 | public void onOpeningComplete() { 259 | if (mListener != null) { 260 | mListener.onOpeningComplete(); 261 | } 262 | } 263 | 264 | public void setColors(int startColor, int endColor) { 265 | mMarker.setColors(startColor, endColor); 266 | } 267 | } 268 | 269 | } 270 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/java/org/adw/library/widgets/discreteseekbar/internal/compat/AnimatorCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Gustavo Claramunt (AnderWeb) 2014. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.adw.library.widgets.discreteseekbar.internal.compat; 18 | 19 | import android.os.Build; 20 | 21 | /** 22 | * Currently, there's no {@link android.animation.ValueAnimator} compatibility version 23 | * and as we didn't want to throw in external dependencies, we made this small class. 24 | * 25 | *26 | * This will work like {@link android.support.v4.view.ViewPropertyAnimatorCompat}, that is, 27 | * not doing anything on API<11 and using the default {@link android.animation.ValueAnimator} 28 | * on API>=11 29 | *
30 | *31 | * This class is used to provide animation to the {@link org.adw.library.widgets.discreteseekbar.DiscreteSeekBar} 32 | * when navigating with the Keypad 33 | *
34 | * 35 | * @hide 36 | */ 37 | public abstract class AnimatorCompat { 38 | public interface AnimationFrameUpdateListener { 39 | public void onAnimationFrame(float currentValue); 40 | } 41 | 42 | AnimatorCompat() { 43 | 44 | } 45 | 46 | public abstract void cancel(); 47 | 48 | public abstract boolean isRunning(); 49 | 50 | public abstract void setDuration(int progressAnimationDuration); 51 | 52 | public abstract void start(); 53 | 54 | public static final AnimatorCompat create(float start, float end, AnimationFrameUpdateListener listener) { 55 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 56 | return new AnimatorCompatV11(start, end, listener); 57 | } else { 58 | return new AnimatorCompatBase(start, end, listener); 59 | } 60 | } 61 | 62 | private static class AnimatorCompatBase extends AnimatorCompat { 63 | 64 | private final AnimationFrameUpdateListener mListener; 65 | private final float mEndValue; 66 | 67 | public AnimatorCompatBase(float start, float end, AnimationFrameUpdateListener listener) { 68 | mListener = listener; 69 | mEndValue = end; 70 | } 71 | 72 | @Override 73 | public void cancel() { 74 | 75 | } 76 | 77 | @Override 78 | public boolean isRunning() { 79 | return false; 80 | } 81 | 82 | @Override 83 | public void setDuration(int progressAnimationDuration) { 84 | 85 | } 86 | 87 | @Override 88 | public void start() { 89 | mListener.onAnimationFrame(mEndValue); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/java/org/adw/library/widgets/discreteseekbar/internal/compat/AnimatorCompatV11.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Gustavo Claramunt (AnderWeb) 2014. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.adw.library.widgets.discreteseekbar.internal.compat; 18 | 19 | import android.animation.ValueAnimator; 20 | import android.annotation.TargetApi; 21 | import android.os.Build; 22 | 23 | /** 24 | * Class to wrap a {@link android.animation.ValueAnimator} 25 | * for use with AnimatorCompat 26 | * 27 | * @hide 28 | * @see {@link org.adw.library.widgets.discreteseekbar.internal.compat.AnimatorCompat} 29 | */ 30 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 31 | public class AnimatorCompatV11 extends AnimatorCompat { 32 | 33 | ValueAnimator animator; 34 | 35 | public AnimatorCompatV11(float start, float end, final AnimationFrameUpdateListener listener) { 36 | super(); 37 | animator = ValueAnimator.ofFloat(start, end); 38 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 39 | @Override 40 | public void onAnimationUpdate(ValueAnimator animation) { 41 | listener.onAnimationFrame((Float) animation.getAnimatedValue()); 42 | } 43 | }); 44 | } 45 | 46 | @Override 47 | public void cancel() { 48 | animator.cancel(); 49 | } 50 | 51 | @Override 52 | public boolean isRunning() { 53 | return animator.isRunning(); 54 | } 55 | 56 | @Override 57 | public void setDuration(int duration) { 58 | animator.setDuration(duration); 59 | } 60 | 61 | @Override 62 | public void start() { 63 | animator.start(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/java/org/adw/library/widgets/discreteseekbar/internal/compat/SeekBarCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Gustavo Claramunt (AnderWeb) 2014. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.adw.library.widgets.discreteseekbar.internal.compat; 18 | 19 | import android.content.res.ColorStateList; 20 | import android.graphics.drawable.Drawable; 21 | import android.os.Build; 22 | import android.support.v4.graphics.drawable.DrawableCompat; 23 | import android.view.View; 24 | import android.view.ViewParent; 25 | import android.widget.TextView; 26 | 27 | import org.adw.library.widgets.discreteseekbar.internal.drawable.AlmostRippleDrawable; 28 | import org.adw.library.widgets.discreteseekbar.internal.drawable.MarkerDrawable; 29 | 30 | /** 31 | * Wrapper compatibility class to call some API-Specific methods 32 | * And offer alternate procedures when possible 33 | * 34 | * @hide 35 | */ 36 | public class SeekBarCompat { 37 | 38 | /** 39 | * Sets the custom Outline provider on API>=21. 40 | * Does nothing on API<21 41 | * 42 | * @param view 43 | * @param markerDrawable 44 | */ 45 | public static void setOutlineProvider(View view, final MarkerDrawable markerDrawable) { 46 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 47 | SeekBarCompatDontCrash.setOutlineProvider(view, markerDrawable); 48 | } 49 | } 50 | 51 | /** 52 | * Our DiscreteSeekBar implementation uses a circular drawable on API < 21 53 | * because we don't set it as Background, but draw it ourselves 54 | * 55 | * @param colorStateList 56 | * @return 57 | */ 58 | public static Drawable getRipple(ColorStateList colorStateList) { 59 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 60 | return SeekBarCompatDontCrash.getRipple(colorStateList); 61 | } else { 62 | return new AlmostRippleDrawable(colorStateList); 63 | } 64 | } 65 | 66 | /** 67 | * As our DiscreteSeekBar implementation uses a circular drawable on API < 21 68 | * we want to use the same method to set its bounds as the Ripple's hotspot bounds. 69 | * 70 | * @param drawable 71 | * @param left 72 | * @param top 73 | * @param right 74 | * @param bottom 75 | */ 76 | public static void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom) { 77 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 78 | //We don't want the full size rect, Lollipop ripple would be too big 79 | int size = (right - left) / 8; 80 | DrawableCompat.setHotspotBounds(drawable, left + size, top + size, right - size, bottom - size); 81 | } else { 82 | drawable.setBounds(left, top, right, bottom); 83 | } 84 | } 85 | 86 | /** 87 | * android.support.v4.view.ViewCompat SHOULD include this once and for all!! 88 | * But it doesn't... 89 | * 90 | * @param view 91 | * @param background 92 | */ 93 | @SuppressWarnings("deprecation") 94 | public static void setBackground(View view, Drawable background) { 95 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 96 | SeekBarCompatDontCrash.setBackground(view, background); 97 | } else { 98 | view.setBackgroundDrawable(background); 99 | } 100 | } 101 | 102 | /** 103 | * Sets the TextView text direction attribute when possible 104 | * 105 | * @param textView 106 | * @param textDirection 107 | * @see android.widget.TextView#setTextDirection(int) 108 | */ 109 | public static void setTextDirection(TextView textView, int textDirection) { 110 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 111 | SeekBarCompatDontCrash.setTextDirection(textView, textDirection); 112 | } 113 | } 114 | 115 | public static boolean isInScrollingContainer(ViewParent p) { 116 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 117 | return SeekBarCompatDontCrash.isInScrollingContainer(p); 118 | } 119 | return false; 120 | } 121 | 122 | public static boolean isHardwareAccelerated(View view) { 123 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 124 | return SeekBarCompatDontCrash.isHardwareAccelerated(view); 125 | } 126 | return false; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/java/org/adw/library/widgets/discreteseekbar/internal/compat/SeekBarCompatDontCrash.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Gustavo Claramunt (AnderWeb) 2014. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.adw.library.widgets.discreteseekbar.internal.compat; 18 | 19 | import android.annotation.TargetApi; 20 | import android.content.res.ColorStateList; 21 | import android.graphics.Outline; 22 | import android.graphics.drawable.Drawable; 23 | import android.graphics.drawable.RippleDrawable; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.view.ViewOutlineProvider; 27 | import android.view.ViewParent; 28 | import android.widget.TextView; 29 | 30 | import org.adw.library.widgets.discreteseekbar.internal.drawable.MarkerDrawable; 31 | 32 | /** 33 | * Wrapper compatibility class to call some API-Specific methods 34 | * And offer alternate procedures when possible 35 | * 36 | * @hide 37 | */ 38 | @TargetApi(21) 39 | class SeekBarCompatDontCrash { 40 | public static void setOutlineProvider(View marker, final MarkerDrawable markerDrawable) { 41 | marker.setOutlineProvider(new ViewOutlineProvider() { 42 | @Override 43 | public void getOutline(View view, Outline outline) { 44 | outline.setConvexPath(markerDrawable.getPath()); 45 | } 46 | }); 47 | } 48 | 49 | public static Drawable getRipple(ColorStateList colorStateList) { 50 | return new RippleDrawable(colorStateList, null, null); 51 | } 52 | 53 | public static void setBackground(View view, Drawable background) { 54 | view.setBackground(background); 55 | } 56 | 57 | public static void setTextDirection(TextView number, int textDirection) { 58 | number.setTextDirection(textDirection); 59 | } 60 | 61 | public static boolean isInScrollingContainer(ViewParent p) { 62 | while (p != null && p instanceof ViewGroup) { 63 | if (((ViewGroup) p).shouldDelayChildPressedState()) { 64 | return true; 65 | } 66 | p = p.getParent(); 67 | } 68 | return false; 69 | } 70 | 71 | public static boolean isHardwareAccelerated(View view) { 72 | return view.isHardwareAccelerated(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/java/org/adw/library/widgets/discreteseekbar/internal/drawable/AlmostRippleDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Gustavo Claramunt (AnderWeb) 2014. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.adw.library.widgets.discreteseekbar.internal.drawable; 18 | 19 | import android.content.res.ColorStateList; 20 | import android.graphics.Canvas; 21 | import android.graphics.Color; 22 | import android.graphics.Paint; 23 | import android.graphics.Rect; 24 | import android.graphics.drawable.Animatable; 25 | import android.os.SystemClock; 26 | import android.support.annotation.NonNull; 27 | import android.view.animation.AccelerateDecelerateInterpolator; 28 | import android.view.animation.Interpolator; 29 | 30 | public class AlmostRippleDrawable extends StateDrawable implements Animatable { 31 | private static final long FRAME_DURATION = 1000 / 60; 32 | private static final int ANIMATION_DURATION = 250; 33 | 34 | private static final float INACTIVE_SCALE = 0f; 35 | private static final float ACTIVE_SCALE = 1f; 36 | private float mCurrentScale = INACTIVE_SCALE; 37 | private Interpolator mInterpolator; 38 | private long mStartTime; 39 | private boolean mReverse = false; 40 | private boolean mRunning = false; 41 | private int mDuration = ANIMATION_DURATION; 42 | private float mAnimationInitialValue; 43 | //We don't use colors just with our drawable state because of animations 44 | private int mPressedColor; 45 | private int mFocusedColor; 46 | private int mDisabledColor; 47 | private int mRippleColor; 48 | private int mRippleBgColor; 49 | 50 | public AlmostRippleDrawable(@NonNull ColorStateList tintStateList) { 51 | super(tintStateList); 52 | mInterpolator = new AccelerateDecelerateInterpolator(); 53 | mFocusedColor = tintStateList.getColorForState(new int[]{android.R.attr.state_focused}, 0xFFFF0000); 54 | mPressedColor = tintStateList.getColorForState(new int[]{android.R.attr.state_pressed}, 0xFFFF0000); 55 | mDisabledColor = tintStateList.getColorForState(new int[]{-android.R.attr.state_enabled}, 0xFFFF0000); 56 | } 57 | 58 | @Override 59 | public void doDraw(Canvas canvas, Paint paint) { 60 | Rect bounds = getBounds(); 61 | int size = Math.min(bounds.width(), bounds.height()); 62 | float scale = mCurrentScale; 63 | int rippleColor = mRippleColor; 64 | int bgColor = mRippleBgColor; 65 | float radius = (size / 2); 66 | float radiusAnimated = radius * scale; 67 | if (scale > INACTIVE_SCALE) { 68 | if (bgColor != 0) { 69 | paint.setColor(bgColor); 70 | paint.setAlpha(decreasedAlpha(Color.alpha(bgColor))); 71 | canvas.drawCircle(bounds.centerX(), bounds.centerY(), radius, paint); 72 | } 73 | if (rippleColor != 0) { 74 | paint.setColor(rippleColor); 75 | paint.setAlpha(modulateAlpha(Color.alpha(rippleColor))); 76 | canvas.drawCircle(bounds.centerX(), bounds.centerY(), radiusAnimated, paint); 77 | } 78 | } 79 | } 80 | 81 | private int decreasedAlpha(int alpha) { 82 | int scale = 100 + (100 >> 7); 83 | return alpha * scale >> 8; 84 | } 85 | 86 | @Override 87 | public boolean setState(int[] stateSet) { 88 | int[] oldState = getState(); 89 | boolean oldPressed = false; 90 | for (int i : oldState) { 91 | if (i == android.R.attr.state_pressed) { 92 | oldPressed = true; 93 | } 94 | } 95 | super.setState(stateSet); 96 | boolean focused = false; 97 | boolean pressed = false; 98 | boolean disabled = true; 99 | for (int i : stateSet) { 100 | if (i == android.R.attr.state_focused) { 101 | focused = true; 102 | } else if (i == android.R.attr.state_pressed) { 103 | pressed = true; 104 | } else if (i == android.R.attr.state_enabled) { 105 | disabled = false; 106 | } 107 | } 108 | 109 | if (disabled) { 110 | unscheduleSelf(mUpdater); 111 | mRippleColor = mDisabledColor; 112 | mRippleBgColor = 0; 113 | mCurrentScale = ACTIVE_SCALE / 2; 114 | invalidateSelf(); 115 | } else { 116 | if (pressed) { 117 | animateToPressed(); 118 | mRippleColor = mRippleBgColor = mPressedColor; 119 | } else if (oldPressed) { 120 | mRippleColor = mRippleBgColor = mPressedColor; 121 | animateToNormal(); 122 | } else if (focused) { 123 | mRippleColor = mFocusedColor; 124 | mRippleBgColor = 0; 125 | mCurrentScale = ACTIVE_SCALE; 126 | invalidateSelf(); 127 | } else { 128 | mRippleColor = 0; 129 | mRippleBgColor = 0; 130 | mCurrentScale = INACTIVE_SCALE; 131 | invalidateSelf(); 132 | } 133 | } 134 | return true; 135 | } 136 | 137 | public void animateToPressed() { 138 | unscheduleSelf(mUpdater); 139 | if (mCurrentScale < ACTIVE_SCALE) { 140 | mReverse = false; 141 | mRunning = true; 142 | mAnimationInitialValue = mCurrentScale; 143 | float durationFactor = 1f - ((mAnimationInitialValue - INACTIVE_SCALE) / (ACTIVE_SCALE - INACTIVE_SCALE)); 144 | mDuration = (int) (ANIMATION_DURATION * durationFactor); 145 | mStartTime = SystemClock.uptimeMillis(); 146 | scheduleSelf(mUpdater, mStartTime + FRAME_DURATION); 147 | } 148 | } 149 | 150 | public void animateToNormal() { 151 | unscheduleSelf(mUpdater); 152 | if (mCurrentScale > INACTIVE_SCALE) { 153 | mReverse = true; 154 | mRunning = true; 155 | mAnimationInitialValue = mCurrentScale; 156 | float durationFactor = 1f - ((mAnimationInitialValue - ACTIVE_SCALE) / (INACTIVE_SCALE - ACTIVE_SCALE)); 157 | mDuration = (int) (ANIMATION_DURATION * durationFactor); 158 | mStartTime = SystemClock.uptimeMillis(); 159 | scheduleSelf(mUpdater, mStartTime + FRAME_DURATION); 160 | } 161 | } 162 | 163 | private void updateAnimation(float factor) { 164 | float initial = mAnimationInitialValue; 165 | float destination = mReverse ? INACTIVE_SCALE : ACTIVE_SCALE; 166 | mCurrentScale = initial + (destination - initial) * factor; 167 | invalidateSelf(); 168 | } 169 | 170 | private final Runnable mUpdater = new Runnable() { 171 | 172 | @Override 173 | public void run() { 174 | 175 | long currentTime = SystemClock.uptimeMillis(); 176 | long diff = currentTime - mStartTime; 177 | if (diff < mDuration) { 178 | float interpolation = mInterpolator.getInterpolation((float) diff / (float) mDuration); 179 | scheduleSelf(mUpdater, currentTime + FRAME_DURATION); 180 | updateAnimation(interpolation); 181 | } else { 182 | unscheduleSelf(mUpdater); 183 | mRunning = false; 184 | updateAnimation(1f); 185 | } 186 | } 187 | }; 188 | 189 | @Override 190 | public void start() { 191 | //No-Op. We control our own animation 192 | } 193 | 194 | @Override 195 | public void stop() { 196 | //No-Op. We control our own animation 197 | } 198 | 199 | @Override 200 | public boolean isRunning() { 201 | return mRunning; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/java/org/adw/library/widgets/discreteseekbar/internal/drawable/MarkerDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Gustavo Claramunt (AnderWeb) 2014. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.adw.library.widgets.discreteseekbar.internal.drawable; 18 | 19 | import android.content.res.ColorStateList; 20 | import android.graphics.Canvas; 21 | import android.graphics.Color; 22 | import android.graphics.Matrix; 23 | import android.graphics.Paint; 24 | import android.graphics.Path; 25 | import android.graphics.Rect; 26 | import android.graphics.RectF; 27 | import android.graphics.drawable.Animatable; 28 | import android.os.SystemClock; 29 | import android.support.annotation.NonNull; 30 | import android.view.animation.AccelerateDecelerateInterpolator; 31 | import android.view.animation.Interpolator; 32 | 33 | /** 34 | * Implementation of {@link StateDrawable} to draw a morphing marker symbol. 35 | *36 | * It's basically an implementation of an {@link android.graphics.drawable.Animatable} Drawable with the following details: 37 | *
38 | *31 | * Subclasses should implement {@link #doDraw(android.graphics.Canvas, android.graphics.Paint)} 32 | *
33 | * 34 | * @hide 35 | */ 36 | public abstract class StateDrawable extends Drawable { 37 | private ColorStateList mTintStateList; 38 | private final Paint mPaint; 39 | private int mCurrentColor; 40 | private int mAlpha = 255; 41 | 42 | public StateDrawable(@NonNull ColorStateList tintStateList) { 43 | super(); 44 | mTintStateList = tintStateList; 45 | mCurrentColor = tintStateList.getDefaultColor(); 46 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 47 | } 48 | 49 | @Override 50 | public boolean isStateful() { 51 | return (mTintStateList.isStateful()) || super.isStateful(); 52 | } 53 | 54 | @Override 55 | public boolean setState(int[] stateSet) { 56 | boolean handled = super.setState(stateSet); 57 | handled = updateTint(stateSet) || handled; 58 | return handled; 59 | } 60 | 61 | @Override 62 | public int getOpacity() { 63 | return PixelFormat.TRANSLUCENT; 64 | } 65 | 66 | private boolean updateTint(int[] state) { 67 | final int color = mTintStateList.getColorForState(state, mCurrentColor); 68 | if (color != mCurrentColor) { 69 | mCurrentColor = color; 70 | //We've changed states 71 | invalidateSelf(); 72 | return true; 73 | } 74 | return false; 75 | } 76 | 77 | @Override 78 | public void draw(Canvas canvas) { 79 | mPaint.setColor(mCurrentColor); 80 | int alpha = modulateAlpha(Color.alpha(mCurrentColor)); 81 | mPaint.setAlpha(alpha); 82 | doDraw(canvas, mPaint); 83 | } 84 | 85 | public void setColorStateList(@NonNull ColorStateList tintStateList) { 86 | mTintStateList = tintStateList; 87 | } 88 | 89 | /** 90 | * Subclasses should implement this method to do the actual drawing 91 | * 92 | * @param canvas The current {@link android.graphics.Canvas} to draw into 93 | * @param paint The {@link android.graphics.Paint} preconfigurred with the current 94 | * {@link android.content.res.ColorStateList} color 95 | */ 96 | abstract void doDraw(Canvas canvas, Paint paint); 97 | 98 | @Override 99 | public void setAlpha(int alpha) { 100 | mAlpha = alpha; 101 | invalidateSelf(); 102 | } 103 | 104 | int modulateAlpha(int alpha) { 105 | int scale = mAlpha + (mAlpha >> 7); 106 | return alpha * scale >> 8; 107 | } 108 | 109 | @Override 110 | public int getAlpha() { 111 | return mAlpha; 112 | } 113 | 114 | @Override 115 | public void setColorFilter(ColorFilter cf) { 116 | mPaint.setColorFilter(cf); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/java/org/adw/library/widgets/discreteseekbar/internal/drawable/ThumbDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Gustavo Claramunt (AnderWeb) 2014. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.adw.library.widgets.discreteseekbar.internal.drawable; 18 | 19 | import android.content.res.ColorStateList; 20 | import android.graphics.Canvas; 21 | import android.graphics.Paint; 22 | import android.graphics.Rect; 23 | import android.graphics.drawable.Animatable; 24 | import android.os.SystemClock; 25 | import android.support.annotation.NonNull; 26 | 27 | /** 28 | *30 | * Special {@link org.adw.library.widgets.discreteseekbar.internal.drawable.StateDrawable} implementation 31 | * to draw the Thumb circle. 32 | *
33 | *34 | * It's special because it will stop drawing once the state is pressed/focused BUT only after a small delay. 35 | *
36 | *37 | * This special delay is meant to help avoiding frame glitches while the {@link org.adw.library.widgets.discreteseekbar.internal.Marker} is added to the Window 38 | *
39 | * 40 | * @hide 41 | */ 42 | public class ThumbDrawable extends StateDrawable implements Animatable { 43 | //The current size for this drawable. Must be converted to real DPs 44 | public static final int DEFAULT_SIZE_DP = 12; 45 | private final int mSize; 46 | private boolean mOpen; 47 | private boolean mRunning; 48 | 49 | public ThumbDrawable(@NonNull ColorStateList tintStateList, int size) { 50 | super(tintStateList); 51 | mSize = size; 52 | } 53 | 54 | @Override 55 | public int getIntrinsicWidth() { 56 | return mSize; 57 | } 58 | 59 | @Override 60 | public int getIntrinsicHeight() { 61 | return mSize; 62 | } 63 | 64 | @Override 65 | public void doDraw(Canvas canvas, Paint paint) { 66 | if (!mOpen) { 67 | Rect bounds = getBounds(); 68 | float radius = (mSize / 2); 69 | canvas.drawCircle(bounds.centerX(), bounds.centerY(), radius, paint); 70 | } 71 | } 72 | 73 | public void animateToPressed() { 74 | scheduleSelf(opener, SystemClock.uptimeMillis() + 100); 75 | mRunning = true; 76 | } 77 | 78 | public void animateToNormal() { 79 | mOpen = false; 80 | mRunning = false; 81 | unscheduleSelf(opener); 82 | invalidateSelf(); 83 | } 84 | 85 | private Runnable opener = new Runnable() { 86 | @Override 87 | public void run() { 88 | mOpen = true; 89 | invalidateSelf(); 90 | mRunning = false; 91 | } 92 | }; 93 | 94 | @Override 95 | public void start() { 96 | //NOOP 97 | } 98 | 99 | @Override 100 | public void stop() { 101 | animateToNormal(); 102 | } 103 | 104 | @Override 105 | public boolean isRunning() { 106 | return mRunning; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/java/org/adw/library/widgets/discreteseekbar/internal/drawable/TrackOvalDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Gustavo Claramunt (AnderWeb) 2014. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.adw.library.widgets.discreteseekbar.internal.drawable; 18 | 19 | import android.content.res.ColorStateList; 20 | import android.graphics.Canvas; 21 | import android.graphics.Paint; 22 | import android.graphics.RectF; 23 | import android.support.annotation.NonNull; 24 | 25 | /** 26 | * Simple {@link org.adw.library.widgets.discreteseekbar.internal.drawable.StateDrawable} implementation 27 | * to draw circles/ovals 28 | * 29 | * @hide 30 | */ 31 | public class TrackOvalDrawable extends StateDrawable { 32 | private RectF mRectF = new RectF(); 33 | 34 | public TrackOvalDrawable(@NonNull ColorStateList tintStateList) { 35 | super(tintStateList); 36 | } 37 | 38 | @Override 39 | void doDraw(Canvas canvas, Paint paint) { 40 | mRectF.set(getBounds()); 41 | canvas.drawOval(mRectF, paint); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/java/org/adw/library/widgets/discreteseekbar/internal/drawable/TrackRectDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Gustavo Claramunt (AnderWeb) 2014. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.adw.library.widgets.discreteseekbar.internal.drawable; 18 | 19 | import android.content.res.ColorStateList; 20 | import android.graphics.Canvas; 21 | import android.graphics.Paint; 22 | import android.support.annotation.NonNull; 23 | 24 | /** 25 | * Simple {@link org.adw.library.widgets.discreteseekbar.internal.drawable.StateDrawable} implementation 26 | * to draw rectangles 27 | * 28 | * @hide 29 | */ 30 | public class TrackRectDrawable extends StateDrawable { 31 | public TrackRectDrawable(@NonNull ColorStateList tintStateList) { 32 | super(tintStateList); 33 | } 34 | 35 | @Override 36 | void doDraw(Canvas canvas, Paint paint) { 37 | canvas.drawRect(getBounds(), paint); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /libraries/discreteSeekBar/src/main/res/color/dsb_progress_color_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 |