/*
* Copyright (C) 2010 Cyril Mottier (http://www.cyrilmottier.com)
*
* 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 com.cyrilmottier.android.remotedrawable;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;
import com.cyrilmottier.android.remotedrawable.util.Config;
/**
* A RemoteDrawable is a Drawable that is composed of two other Drawables. The
* first one is a placeholder and the second one is a Drawable that is
* downloaded asyncronously on the network. The placeholder is display as long
* as the distant Drawable is not ready. Once the distant Drawable is ready, it
* replaces the placeholder.
*
* @author cyrilmottier
*/
public class RemoteDrawable extends Drawable implements DrawableDownloader.Callback, Drawable.Callback {
/*
* TODO cyril: This sample code as been created quite rapidly to show
* developers how to create a Drawable. It hasn't been tested at all and
* therefore many bugs and not-implemented methods may exists. For instance
* it's not working properly during orientation changes.
*/
private static final boolean DEBUG_LOGS_FILE_ENABLED = true;
private static final boolean DEBUG_LOGS_ENABLED = DEBUG_LOGS_FILE_ENABLED && Config.DEBUG_LOGS_PROJECT_ENABLED;
private static final String LOG_TAG = RemoteDrawable.class.getSimpleName();
private RemoteState mRemoteState;
private Drawable mCurrentDrawable;
private boolean mMutated;
public RemoteDrawable(Drawable placeholder, String URL) {
Drawable d = DrawableDownloader.getInstance().getDrawable(URL, this);
mCurrentDrawable = (d == null) ? placeholder : d;
mCurrentDrawable.setCallback(this);
mRemoteState = new RemoteState();
}
@Override
public void draw(Canvas canvas) {
if (mCurrentDrawable != null) {
mCurrentDrawable.draw(canvas);
}
}
@Override
public void setAlpha(int alpha) {
if (alpha != mRemoteState.mAlpha) {
mRemoteState.mAlpha = alpha;
if (mCurrentDrawable != null) {
mCurrentDrawable.setAlpha(alpha);
}
}
}
@Override
public void setColorFilter(ColorFilter cf) {
if (cf != mRemoteState.mColorFilter) {
mRemoteState.mColorFilter = cf;
if (mCurrentDrawable != null) {
mCurrentDrawable.setColorFilter(cf);
}
}
}
@Override
public void setDither(boolean dither) {
if (mRemoteState.mDither != dither) {
mRemoteState.mDither = dither;
if (mCurrentDrawable != null) {
mCurrentDrawable.setDither(dither);
}
}
}
@Override
public void setFilterBitmap(boolean filter) {
if (mRemoteState.mFilter != filter) {
mRemoteState.mFilter = filter;
if (mCurrentDrawable != null) {
mCurrentDrawable.setFilterBitmap(filter);
}
}
}
@Override
public Drawable getCurrent() {
return mCurrentDrawable;
}
@Override
protected void onBoundsChange(Rect bounds) {
if (mCurrentDrawable != null) {
mCurrentDrawable.setBounds(bounds);
}
}
@Override
public boolean isStateful() {
if (mCurrentDrawable != null) {
return mCurrentDrawable.isStateful();
}
return false;
}
@Override
protected boolean onStateChange(int[] state) {
if (mCurrentDrawable != null) {
return mCurrentDrawable.setState(state);
}
return false;
}
@Override
protected boolean onLevelChange(int level) {
if (mCurrentDrawable != null) {
return mCurrentDrawable.setLevel(level);
}
return false;
}
@Override
public int getIntrinsicWidth() {
return mCurrentDrawable != null ? mCurrentDrawable.getIntrinsicWidth() : -1;
}
@Override
public int getIntrinsicHeight() {
return mCurrentDrawable != null ? mCurrentDrawable.getIntrinsicHeight() : -1;
}
@Override
public int getMinimumWidth() {
return mCurrentDrawable != null ? mCurrentDrawable.getMinimumWidth() : 0;
}
@Override
public int getMinimumHeight() {
return mCurrentDrawable != null ? mCurrentDrawable.getMinimumHeight() : 0;
}
@Override
public int getOpacity() {
/*
* Here PixelFormat.TRANSPARENT is returned because the getOpacity()
* method has to be as conservative as possible. Indeed, we don't know
* the opacity of the remote Drawable ...
*/
return PixelFormat.TRANSPARENT;
}
@Override
public boolean getPadding(Rect padding) {
return mCurrentDrawable == null ? false : mCurrentDrawable.getPadding(padding);
}
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mRemoteState = new RemoteState(mRemoteState);
mMutated = true;
}
return this;
}
@Override
public final ConstantState getConstantState() {
mRemoteState.mChangingConfigurations = super.getChangingConfigurations();
return mRemoteState;
}
final static class RemoteState extends Drawable.ConstantState {
int mAlpha;
ColorFilter mColorFilter;
boolean mDither;
boolean mFilter;
int mChangingConfigurations;
public RemoteState() {
mAlpha = 0xFF;
mColorFilter = null;
mDither = true;
mFilter = true;
}
public RemoteState(RemoteState state) {
mAlpha = state.mAlpha;
mColorFilter = state.mColorFilter;
mDither = state.mDither;
mFilter = state.mFilter;
mChangingConfigurations = state.mChangingConfigurations;
}
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
}
@Override
public Drawable newDrawable() {
// TODO cyril: Should return a Drawable that is a copy of the
// current Drawable (same placeholder as well as URL)
return null;
}
}
public void onDrawableLoadingFailed(String urlString) {
if (DEBUG_LOGS_ENABLED) {
Log.d(LOG_TAG, "onDrawableLoadingFailed: " + urlString);
}
// TODO cyril : What to do here? Doing nothing seems like a great idea
// as it keep the placeholder...
}
public void onDrawableLoaded(String urlString, final Drawable drawable) {
Rect bounds = mCurrentDrawable.getBounds();
if (DEBUG_LOGS_ENABLED) {
Log.d(LOG_TAG, "Bounds are : " + bounds);
}
drawable.setBounds(mCurrentDrawable.getBounds());
mCurrentDrawable = drawable;
invalidateSelf();
}
public void invalidateDrawable(Drawable who) {
if (who == mCurrentDrawable) {
invalidateSelf();
}
}
public void scheduleDrawable(Drawable who, Runnable what, long when) {
scheduleSelf(what, when);
}
public void unscheduleDrawable(Drawable who, Runnable what) {
unscheduleSelf(what);
}
}