diff --git a/src/api-impl/android/graphics/drawable/ClipDrawable.java b/src/api-impl/android/graphics/drawable/ClipDrawable.java new file mode 100644 index 00000000..d35f3ace --- /dev/null +++ b/src/api-impl/android/graphics/drawable/ClipDrawable.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2006 The Android Open Source 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 android.graphics.drawable; + +import android.annotation.NonNull; +import android.annotation.Nullable; +//import android.compat.annotation.UnsupportedAppUsage; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.Gravity; +import com.android.internal.R; +import java.io.IOException; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * A Drawable that clips another Drawable based on this Drawable's current + * level value. You can control how much the child Drawable gets clipped in width + * and height based on the level, as well as a gravity to control where it is + * placed in its overall container. Most often used to implement things like + * progress bars, by increasing the drawable's level with {@link + * android.graphics.drawable.Drawable#setLevel(int) setLevel()}. + *
Note: The drawable is clipped completely and not visible when + * the level is 0 and fully revealed when the level is 10,000.
+ * + *It can be defined in an XML file with the <clip> element. For more
+ * information, see the guide to Drawable Resources.
+ * Implementing subclasses should call through to the super method first. + * + * @param a the typed array rom which properties should be read + */ + private void updateStateFromTypedArray(@NonNull TypedArray a) { + final DrawableWrapperState state = mState; + if (state == null) { + return; + } + + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); + + // Extract the theme attributes, if any. + state.mThemeAttrs = a.extractThemeAttrs(); + + if (a.hasValueOrEmpty(R.styleable.DrawableWrapper_drawable)) { + setDrawable(a.getDrawable(R.styleable.DrawableWrapper_drawable)); + } + } + + /*@Override + public boolean canApplyTheme() { + return (mState != null && mState.canApplyTheme()) || super.canApplyTheme(); + }*/ + + @Override + public void invalidateDrawable(@NonNull Drawable who) { + final Callback callback = getCallback(); + if (callback != null) { + callback.invalidateDrawable(this); + } + } + + @Override + public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { + final Callback callback = getCallback(); + if (callback != null) { + callback.scheduleDrawable(this, what, when); + } + } + + @Override + public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { + final Callback callback = getCallback(); + if (callback != null) { + callback.unscheduleDrawable(this, what); + } + } + + @Override + public void draw(@NonNull Canvas canvas) { + if (mDrawable != null) { + mDrawable.draw(canvas); + } + } + + @Override + public /*@Config*/ int getChangingConfigurations() { + return super.getChangingConfigurations() | (mState != null ? mState.getChangingConfigurations() : 0) | mDrawable.getChangingConfigurations(); + } + + @Override + public boolean getPadding(@NonNull Rect padding) { + return mDrawable != null && mDrawable.getPadding(padding); + } + + /*@Override + public Insets getOpticalInsets() { + return mDrawable != null ? mDrawable.getOpticalInsets() : Insets.NONE; + }*/ + + @Override + public void setHotspot(float x, float y) { + if (mDrawable != null) { + mDrawable.setHotspot(x, y); + } + } + + /*@Override + public void setHotspotBounds(int left, int top, int right, int bottom) { + if (mDrawable != null) { + mDrawable.setHotspotBounds(left, top, right, bottom); + } + }*/ + + /*@Override + public void getHotspotBounds(@NonNull Rect outRect) { + if (mDrawable != null) { + mDrawable.getHotspotBounds(outRect); + } else { + outRect.set(getBounds()); + } + }*/ + + @Override + public boolean setVisible(boolean visible, boolean restart) { + final boolean superChanged = super.setVisible(visible, restart); + final boolean changed = mDrawable != null && mDrawable.setVisible(visible, restart); + return superChanged | changed; + } + + @Override + public void setAlpha(int alpha) { + if (mDrawable != null) { + mDrawable.setAlpha(alpha); + } + } + + /*@Override + public int getAlpha() { + return mDrawable != null ? mDrawable.getAlpha() : 255; + }*/ + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + if (mDrawable != null) { + mDrawable.setColorFilter(colorFilter); + } + } + + /*@Override + public ColorFilter getColorFilter() { + final Drawable drawable = getDrawable(); + if (drawable != null) { + return drawable.getColorFilter(); + } + return super.getColorFilter(); + }*/ + + @Override + public void setTintList(@Nullable ColorStateList tint) { + if (mDrawable != null) { + mDrawable.setTintList(tint); + } + } + + /*@Override + public void setTintBlendMode(@NonNull BlendMode blendMode) { + if (mDrawable != null) { + mDrawable.setTintBlendMode(blendMode); + } + }*/ + + /*@Override + public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) { + return mDrawable != null && mDrawable.setLayoutDirection(layoutDirection); + }*/ + + /*@Override + public int getOpacity() { + return mDrawable != null ? mDrawable.getOpacity() : PixelFormat.TRANSPARENT; + }*/ + + @Override + public boolean isStateful() { + return mDrawable != null && mDrawable.isStateful(); + } + + /*@Override + public boolean hasFocusStateSpecified() { + return mDrawable != null && mDrawable.hasFocusStateSpecified(); + }*/ + + @Override + protected boolean onStateChange(@NonNull int[] state) { + if (mDrawable != null && mDrawable.isStateful()) { + final boolean changed = mDrawable.setState(state); + if (changed) { + onBoundsChange(getBounds()); + } + return changed; + } + return false; + } + + @Override + public void jumpToCurrentState() { + if (mDrawable != null) { + mDrawable.jumpToCurrentState(); + } + } + + /*@Override + protected boolean onLevelChange(int level) { + return mDrawable != null && mDrawable.setLevel(level); + }*/ + + @Override + protected void onBoundsChange(@NonNull Rect bounds) { + if (mDrawable != null) { + mDrawable.setBounds(bounds); + } + } + + @Override + public int getIntrinsicWidth() { + return mDrawable != null ? mDrawable.getIntrinsicWidth() : -1; + } + + @Override + public int getIntrinsicHeight() { + return mDrawable != null ? mDrawable.getIntrinsicHeight() : -1; + } + + /*@Override + public void getOutline(@NonNull Outline outline) { + if (mDrawable != null) { + mDrawable.getOutline(outline); + } else { + super.getOutline(outline); + } + }*/ + + /*@Override + @Nullable + public ConstantState getConstantState() { + if (mState != null && mState.canConstantState()) { + mState.mChangingConfigurations = getChangingConfigurations(); + return mState; + } + return null; + }*/ + + @Override + @NonNull + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mState = mutateConstantState(); + if (mDrawable != null) { + mDrawable.mutate(); + } + if (mState != null) { + mState.mDrawableState = mDrawable != null ? mDrawable.getConstantState() : null; + } + mMutated = true; + } + return this; + } + + /** + * Mutates the constant state and returns the new state. Responsible for + * updating any local copy. + *
+ * This method should never call the super implementation; it should always + * mutate and return its own constant state. + * + * @return the new state + */ + DrawableWrapperState mutateConstantState() { + return mState; + } + + /** + * @hide Only used by the framework for pre-loading resources. + */ + /*public void clearMutated() { + super.clearMutated(); + if (mDrawable != null) { + mDrawable.clearMutated(); + } + mMutated = false; + }*/ + + /** + * Called during inflation to inflate the child element. The last valid + * child element will take precedence over any other child elements or + * explicit drawable attribute. + */ + /* void inflateChildDrawable(@NonNull Resources r, @NonNull XmlPullParser parser, + @NonNull AttributeSet attrs, @Nullable Theme theme) + throws XmlPullParserException, IOException { + // Seek to the first child element. + Drawable dr = null; + int type; + final int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.START_TAG) { + dr = Drawable.createFromXmlInnerForDensity(r, parser, attrs, + mState.mSrcDensityOverride, theme); + } + } + + if (dr != null) { + setDrawable(dr); + } + }*/ + + abstract static class DrawableWrapperState extends Drawable.ConstantState { + private int[] mThemeAttrs; + + //@Config + int mChangingConfigurations; + int mDensity = DisplayMetrics.DENSITY_DEFAULT; + + /** + * The density to use when looking up resources from + * {@link Resources#getDrawableForDensity(int, int, Theme)}. + * A value of 0 means there is no override and the system density will be used. + * @hide + */ + int mSrcDensityOverride = 0; + + Drawable.ConstantState mDrawableState; + + DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) { + if (orig != null) { + mThemeAttrs = orig.mThemeAttrs; + mChangingConfigurations = orig.mChangingConfigurations; + mDrawableState = orig.mDrawableState; + mSrcDensityOverride = orig.mSrcDensityOverride; + } + + final int density; + if (res != null) { + density = res.getDisplayMetrics().densityDpi; + } else if (orig != null) { + density = orig.mDensity; + } else { + density = 0; + } + + mDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density; + } + + /** + * Sets the constant state density. + *
+ * If the density has been previously set, dispatches the change to + * subclasses so that density-dependent properties may be scaled as + * necessary. + * + * @param targetDensity the new constant state density + */ + public final void setDensity(int targetDensity) { + if (mDensity != targetDensity) { + final int sourceDensity = mDensity; + mDensity = targetDensity; + + onDensityChanged(sourceDensity, targetDensity); + } + } + + /** + * Called when the constant state density changes. + *
+ * Subclasses with density-dependent constant state properties should + * override this method and scale their properties as necessary. + * + * @param sourceDensity the previous constant state density + * @param targetDensity the new constant state density + */ + void onDensityChanged(int sourceDensity, int targetDensity) { + // Stub method. + } + + /*@Override + public boolean canApplyTheme() { + return mThemeAttrs != null || (mDrawableState != null && mDrawableState.canApplyTheme()) || super.canApplyTheme(); + }*/ + + @Override + public Drawable newDrawable() { + return newDrawable(null); + } + + @Override + public abstract Drawable newDrawable(@Nullable Resources res); + + @Override + public /*@Config*/ int getChangingConfigurations() { + return mChangingConfigurations | (mDrawableState != null ? mDrawableState.getChangingConfigurations() : 0); + } + + public boolean canConstantState() { + return mDrawableState != null; + } } }