Files
frameworks_base/graphics/java/android/graphics/drawable/DrawableContainer.java
Phil Dubach 6be507cc66 Fix NullPointerException in DrawableContainer.mutate()
DrawableContainerState.mDrawables is an array which may be only partially
filled, as can be seen in the constructor and the addChild() method.

DrawableContainer.mutate() wrongly assumed that the array does not contain
null references.
2009-06-29 18:31:17 -07:00

504 lines
15 KiB
Java

/*
* 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.graphics.*;
public class DrawableContainer extends Drawable implements Drawable.Callback {
private DrawableContainerState mDrawableContainerState;
private Drawable mCurrDrawable;
private int mAlpha = 0xFF;
private ColorFilter mColorFilter;
private boolean mDither;
private int mCurIndex = -1;
private boolean mMutated;
// overrides from Drawable
@Override
public void draw(Canvas canvas) {
if (mCurrDrawable != null) {
mCurrDrawable.draw(canvas);
}
}
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations()
| mDrawableContainerState.mChangingConfigurations
| mDrawableContainerState.mChildrenChangingConfigurations;
}
@Override
public boolean getPadding(Rect padding) {
final Rect r = mDrawableContainerState.getConstantPadding();
if (r != null) {
padding.set(r);
return true;
}
if (mCurrDrawable != null) {
return mCurrDrawable.getPadding(padding);
} else {
return super.getPadding(padding);
}
}
@Override
public void setAlpha(int alpha) {
if (mAlpha != alpha) {
mAlpha = alpha;
if (mCurrDrawable != null) {
mCurrDrawable.setAlpha(alpha);
}
}
}
@Override
public void setDither(boolean dither) {
if (mDither != dither) {
mDither = dither;
if (mCurrDrawable != null) {
mCurrDrawable.setDither(mDither);
}
}
}
@Override
public void setColorFilter(ColorFilter cf) {
if (mColorFilter != cf) {
mColorFilter = cf;
if (mCurrDrawable != null) {
mCurrDrawable.setColorFilter(cf);
}
}
}
@Override
protected void onBoundsChange(Rect bounds) {
if (mCurrDrawable != null) {
mCurrDrawable.setBounds(bounds);
}
}
@Override
public boolean isStateful() {
return mDrawableContainerState.isStateful();
}
@Override
protected boolean onStateChange(int[] state) {
if (mCurrDrawable != null) {
return mCurrDrawable.setState(state);
}
return false;
}
@Override
protected boolean onLevelChange(int level) {
if (mCurrDrawable != null) {
return mCurrDrawable.setLevel(level);
}
return false;
}
@Override
public int getIntrinsicWidth() {
if (mDrawableContainerState.isConstantSize()) {
return mDrawableContainerState.getConstantWidth();
}
return mCurrDrawable != null ? mCurrDrawable.getIntrinsicWidth() : -1;
}
@Override
public int getIntrinsicHeight() {
if (mDrawableContainerState.isConstantSize()) {
return mDrawableContainerState.getConstantHeight();
}
return mCurrDrawable != null ? mCurrDrawable.getIntrinsicHeight() : -1;
}
@Override
public int getMinimumWidth() {
if (mDrawableContainerState.isConstantSize()) {
return mDrawableContainerState.getConstantMinimumWidth();
}
return mCurrDrawable != null ? mCurrDrawable.getMinimumWidth() : 0;
}
@Override
public int getMinimumHeight() {
if (mDrawableContainerState.isConstantSize()) {
return mDrawableContainerState.getConstantMinimumHeight();
}
return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0;
}
public void invalidateDrawable(Drawable who)
{
if (who == mCurrDrawable && mCallback != null) {
mCallback.invalidateDrawable(this);
}
}
public void scheduleDrawable(Drawable who, Runnable what, long when)
{
if (who == mCurrDrawable && mCallback != null) {
mCallback.scheduleDrawable(this, what, when);
}
}
public void unscheduleDrawable(Drawable who, Runnable what)
{
if (who == mCurrDrawable && mCallback != null) {
mCallback.unscheduleDrawable(this, what);
}
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
boolean changed = super.setVisible(visible, restart);
if (mCurrDrawable != null) {
mCurrDrawable.setVisible(visible, restart);
}
return changed;
}
@Override
public int getOpacity() {
return mCurrDrawable == null || !mCurrDrawable.isVisible() ? PixelFormat.TRANSPARENT :
mDrawableContainerState.getOpacity();
}
public boolean selectDrawable(int idx)
{
if (idx == mCurIndex) {
return false;
}
if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
Drawable d = mDrawableContainerState.mDrawables[idx];
if (mCurrDrawable != null) {
mCurrDrawable.setVisible(false, false);
}
mCurrDrawable = d;
mCurIndex = idx;
if (d != null) {
d.setVisible(isVisible(), true);
d.setAlpha(mAlpha);
d.setDither(mDither);
d.setColorFilter(mColorFilter);
d.setState(getState());
d.setLevel(getLevel());
d.setBounds(getBounds());
}
} else {
if (mCurrDrawable != null) {
mCurrDrawable.setVisible(false, false);
}
mCurrDrawable = null;
mCurIndex = -1;
}
invalidateSelf();
return true;
}
@Override
public Drawable getCurrent() {
return mCurrDrawable;
}
@Override
public ConstantState getConstantState() {
if (mDrawableContainerState.canConstantState()) {
mDrawableContainerState.mChangingConfigurations = super.getChangingConfigurations();
return mDrawableContainerState;
}
return null;
}
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
final int N = mDrawableContainerState.getChildCount();
final Drawable[] drawables = mDrawableContainerState.getChildren();
for (int i = 0; i < N; i++) {
drawables[i].mutate();
}
mMutated = true;
}
return this;
}
public abstract static class DrawableContainerState extends ConstantState {
final DrawableContainer mOwner;
int mChangingConfigurations;
int mChildrenChangingConfigurations;
Drawable[] mDrawables;
int mNumChildren;
boolean mVariablePadding = false;
Rect mConstantPadding = null;
boolean mConstantSize = false;
boolean mComputedConstantSize = false;
int mConstantWidth;
int mConstantHeight;
int mConstantMinimumWidth;
int mConstantMinimumHeight;
boolean mHaveOpacity = false;
int mOpacity;
boolean mHaveStateful = false;
boolean mStateful;
boolean mCheckedConstantState;
boolean mCanConstantState;
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner) {
mOwner = owner;
if (orig != null) {
mChangingConfigurations = orig.mChangingConfigurations;
mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
final Drawable[] origDr = orig.mDrawables;
mDrawables = new Drawable[origDr.length];
mNumChildren = orig.mNumChildren;
final int N = mNumChildren;
for (int i=0; i<N; i++) {
mDrawables[i] = origDr[i].getConstantState().newDrawable();
mDrawables[i].setCallback(owner);
}
mCheckedConstantState = mCanConstantState = true;
mVariablePadding = orig.mVariablePadding;
if (orig.mConstantPadding != null) {
mConstantPadding = new Rect(orig.mConstantPadding);
}
mConstantSize = orig.mConstantSize;
mComputedConstantSize = orig.mComputedConstantSize;
mConstantWidth = orig.mConstantWidth;
mConstantHeight = orig.mConstantHeight;
mHaveOpacity = orig.mHaveOpacity;
mOpacity = orig.mOpacity;
mHaveStateful = orig.mHaveStateful;
mStateful = orig.mStateful;
} else {
mDrawables = new Drawable[10];
mNumChildren = 0;
mCheckedConstantState = mCanConstantState = false;
}
}
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
}
public final int addChild(Drawable dr) {
final int pos = mNumChildren;
if (pos >= mDrawables.length) {
growArray(pos, pos+10);
}
dr.setVisible(false, true);
dr.setCallback(mOwner);
mDrawables[pos] = dr;
mNumChildren++;
mChildrenChangingConfigurations |= dr.getChangingConfigurations();
mHaveOpacity = false;
mHaveStateful = false;
mConstantPadding = null;
mComputedConstantSize = false;
return pos;
}
public final int getChildCount() {
return mNumChildren;
}
public final Drawable[] getChildren() {
return mDrawables;
}
/** A boolean value indicating whether to use the maximum padding value of
* all frames in the set (false), or to use the padding value of the frame
* being shown (true). Default value is false.
*/
public final void setVariablePadding(boolean variable) {
mVariablePadding = variable;
}
public final Rect getConstantPadding() {
if (mVariablePadding) {
return null;
}
if (mConstantPadding != null) {
return mConstantPadding;
}
final Rect r = new Rect(0, 0, 0, 0);
final Rect t = new Rect();
final int N = getChildCount();
final Drawable[] drawables = mDrawables;
for (int i = 0; i < N; i++) {
if (drawables[i].getPadding(t)) {
if (t.left > r.left) r.left = t.left;
if (t.top > r.top) r.top = t.top;
if (t.right > r.right) r.right = t.right;
if (t.bottom > r.bottom) r.bottom = t.bottom;
}
}
return (mConstantPadding=r);
}
public final void setConstantSize(boolean constant) {
mConstantSize = constant;
}
public final boolean isConstantSize() {
return mConstantSize;
}
public final int getConstantWidth() {
if (!mComputedConstantSize) {
computeConstantSize();
}
return mConstantWidth;
}
public final int getConstantHeight() {
if (!mComputedConstantSize) {
computeConstantSize();
}
return mConstantHeight;
}
public final int getConstantMinimumWidth() {
if (!mComputedConstantSize) {
computeConstantSize();
}
return mConstantMinimumWidth;
}
public final int getConstantMinimumHeight() {
if (!mComputedConstantSize) {
computeConstantSize();
}
return mConstantMinimumHeight;
}
private void computeConstantSize() {
mComputedConstantSize = true;
final int N = getChildCount();
final Drawable[] drawables = mDrawables;
mConstantWidth = mConstantHeight = 0;
mConstantMinimumWidth = mConstantMinimumHeight = 0;
for (int i = 0; i < N; i++) {
Drawable dr = drawables[i];
int s = dr.getIntrinsicWidth();
if (s > mConstantWidth) mConstantWidth = s;
s = dr.getIntrinsicHeight();
if (s > mConstantHeight) mConstantHeight = s;
s = dr.getMinimumWidth();
if (s > mConstantMinimumWidth) mConstantMinimumWidth = s;
s = dr.getMinimumHeight();
if (s > mConstantMinimumHeight) mConstantMinimumHeight = s;
}
}
public final int getOpacity() {
if (mHaveOpacity) {
return mOpacity;
}
final int N = getChildCount();
final Drawable[] drawables = mDrawables;
int op = N > 0 ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
for (int i = 1; i < N; i++) {
op = Drawable.resolveOpacity(op, drawables[i].getOpacity());
}
mOpacity = op;
mHaveOpacity = true;
return op;
}
public final boolean isStateful() {
if (mHaveStateful) {
return mStateful;
}
boolean stateful = false;
final int N = getChildCount();
for (int i = 0; i < N; i++) {
if (mDrawables[i].isStateful()) {
stateful = true;
break;
}
}
mStateful = stateful;
mHaveStateful = true;
return stateful;
}
public void growArray(int oldSize, int newSize) {
Drawable[] newDrawables = new Drawable[newSize];
System.arraycopy(mDrawables, 0, newDrawables, 0, oldSize);
mDrawables = newDrawables;
}
public synchronized boolean canConstantState() {
if (!mCheckedConstantState) {
mCanConstantState = true;
final int N = mNumChildren;
for (int i=0; i<N; i++) {
if (mDrawables[i].getConstantState() == null) {
mCanConstantState = false;
break;
}
}
mCheckedConstantState = true;
}
return mCanConstantState;
}
}
protected void setConstantState(DrawableContainerState state)
{
mDrawableContainerState = state;
}
}