public SoundLevels(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); // Safe source, replaced with system one when attached. mLevelSource = new AudioLevelSource(); mLevelSource.setSpeechLevel(0); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SoundLevels, defStyle, 0); mMaximumLevelSize = a.getDimensionPixelOffset(R.styleable.SoundLevels_maxLevelRadius, 0); mMinimumLevelSize = a.getDimensionPixelOffset(R.styleable.SoundLevels_minLevelRadius, 0); mMinimumLevel = mMinimumLevelSize / mMaximumLevelSize; mPrimaryLevelPaint = new Paint(); mPrimaryLevelPaint.setColor(a.getColor(R.styleable.SoundLevels_primaryColor, Color.BLACK)); mPrimaryLevelPaint.setFlags(Paint.ANTI_ALIAS_FLAG); a.recycle(); // This animator generates ticks that invalidate the // view so that the animation is synced with the global animation loop. // TODO: We could probably remove this in favor of using postInvalidateOnAnimation // which might improve things further. mSpeechLevelsAnimator = new TimeAnimator(); mSpeechLevelsAnimator.setRepeatCount(ObjectAnimator.INFINITE); mSpeechLevelsAnimator.setTimeListener( new TimeListener() { @Override public void onTimeUpdate( final TimeAnimator animation, final long totalTime, final long deltaTime) { invalidate(); } }); }