1/* 2 * Copyright (C) 2015 The Android Open Source Project 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 17package android.support.v7.widget; 18 19import android.graphics.Bitmap; 20import android.graphics.BitmapShader; 21import android.graphics.Shader; 22import android.graphics.drawable.AnimationDrawable; 23import android.graphics.drawable.BitmapDrawable; 24import android.graphics.drawable.ClipDrawable; 25import android.graphics.drawable.Drawable; 26import android.graphics.drawable.LayerDrawable; 27import android.graphics.drawable.ShapeDrawable; 28import android.graphics.drawable.shapes.RoundRectShape; 29import android.graphics.drawable.shapes.Shape; 30import android.support.v4.graphics.drawable.DrawableWrapper; 31import android.util.AttributeSet; 32import android.view.Gravity; 33import android.widget.ProgressBar; 34 35class AppCompatProgressBarHelper { 36 37 private static final int[] TINT_ATTRS = { 38 android.R.attr.indeterminateDrawable, 39 android.R.attr.progressDrawable 40 }; 41 42 private final ProgressBar mView; 43 final AppCompatDrawableManager mDrawableManager; 44 45 private Bitmap mSampleTile; 46 47 AppCompatProgressBarHelper(ProgressBar view, AppCompatDrawableManager drawableManager) { 48 mView = view; 49 mDrawableManager = drawableManager; 50 } 51 52 void loadFromAttributes(AttributeSet attrs, int defStyleAttr) { 53 TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs, 54 TINT_ATTRS, defStyleAttr, 0); 55 56 Drawable drawable = a.getDrawableIfKnown(0); 57 if (drawable != null) { 58 mView.setIndeterminateDrawable(tileifyIndeterminate(drawable)); 59 } 60 61 drawable = a.getDrawableIfKnown(1); 62 if (drawable != null) { 63 mView.setProgressDrawable(tileify(drawable, false)); 64 } 65 66 a.recycle(); 67 } 68 69 /** 70 * Converts a drawable to a tiled version of itself. It will recursively 71 * traverse layer and state list drawables. 72 */ 73 private Drawable tileify(Drawable drawable, boolean clip) { 74 if (drawable instanceof DrawableWrapper) { 75 Drawable inner = ((DrawableWrapper) drawable).getWrappedDrawable(); 76 if (inner != null) { 77 inner = tileify(inner, clip); 78 ((DrawableWrapper) drawable).setWrappedDrawable(inner); 79 } 80 } else if (drawable instanceof LayerDrawable) { 81 LayerDrawable background = (LayerDrawable) drawable; 82 final int N = background.getNumberOfLayers(); 83 Drawable[] outDrawables = new Drawable[N]; 84 85 for (int i = 0; i < N; i++) { 86 int id = background.getId(i); 87 outDrawables[i] = tileify(background.getDrawable(i), 88 (id == android.R.id.progress || id == android.R.id.secondaryProgress)); 89 } 90 LayerDrawable newBg = new LayerDrawable(outDrawables); 91 92 for (int i = 0; i < N; i++) { 93 newBg.setId(i, background.getId(i)); 94 } 95 96 return newBg; 97 98 } else if (drawable instanceof BitmapDrawable) { 99 final BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; 100 final Bitmap tileBitmap = bitmapDrawable.getBitmap(); 101 if (mSampleTile == null) { 102 mSampleTile = tileBitmap; 103 } 104 105 final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape()); 106 final BitmapShader bitmapShader = new BitmapShader(tileBitmap, 107 Shader.TileMode.REPEAT, Shader.TileMode.CLAMP); 108 shapeDrawable.getPaint().setShader(bitmapShader); 109 shapeDrawable.getPaint().setColorFilter(bitmapDrawable.getPaint().getColorFilter()); 110 return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT, 111 ClipDrawable.HORIZONTAL) : shapeDrawable; 112 } 113 114 return drawable; 115 } 116 117 /** 118 * Convert a AnimationDrawable for use as a barberpole animation. 119 * Each frame of the animation is wrapped in a ClipDrawable and 120 * given a tiling BitmapShader. 121 */ 122 private Drawable tileifyIndeterminate(Drawable drawable) { 123 if (drawable instanceof AnimationDrawable) { 124 AnimationDrawable background = (AnimationDrawable) drawable; 125 final int N = background.getNumberOfFrames(); 126 AnimationDrawable newBg = new AnimationDrawable(); 127 newBg.setOneShot(background.isOneShot()); 128 129 for (int i = 0; i < N; i++) { 130 Drawable frame = tileify(background.getFrame(i), true); 131 frame.setLevel(10000); 132 newBg.addFrame(frame, background.getDuration(i)); 133 } 134 newBg.setLevel(10000); 135 drawable = newBg; 136 } 137 return drawable; 138 } 139 140 private Shape getDrawableShape() { 141 final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 }; 142 return new RoundRectShape(roundedCorners, null, null); 143 } 144 145 Bitmap getSampleTime() { 146 return mSampleTile; 147 } 148 149} 150