1/*
2 * Copyright (C) 2013 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 com.android.bitmap.view;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.drawable.Drawable;
22import android.net.Uri;
23import android.os.Build;
24import android.util.AttributeSet;
25import android.widget.ImageView;
26
27import com.android.bitmap.drawable.BasicBitmapDrawable;
28
29/**
30 * A helpful ImageView replacement that can generally be used in lieu of ImageView.
31 * BitmapDrawableImageView has logic to unbind its BasicBitmapDrawable when it is detached from the
32 * window.
33 */
34public class BitmapDrawableImageView extends ImageView {
35    private static final boolean HAS_TRANSIENT_STATE_SUPPORTED =
36        Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
37    private static final boolean TEMPORARY = true;
38    private static final boolean PERMANENT = !TEMPORARY;
39
40    private BasicBitmapDrawable mDrawable;
41    private boolean mAttachedToWindow;
42
43    public BitmapDrawableImageView(final Context context) {
44        this(context, null);
45    }
46
47    public BitmapDrawableImageView(final Context context, final AttributeSet attrs) {
48        this(context, attrs, 0);
49    }
50
51    public BitmapDrawableImageView(final Context context, final AttributeSet attrs,
52            final int defStyle) {
53        super(context, attrs, defStyle);
54    }
55
56    /**
57     * Get the source drawable for this BitmapDrawableImageView.
58     * @return The source drawable casted to the given type, or null if the type does not match.
59     */
60    @SuppressWarnings("unchecked") // Cast to type parameter.
61    public <E extends BasicBitmapDrawable> E getTypedDrawable() {
62        try {
63            return (E) mDrawable;
64        } catch (Exception ignored) {
65            return null;
66        }
67    }
68
69    /**
70     * Set the given drawable as the source for this BitmapDrawableImageView.
71     * @param drawable The source drawable.
72     */
73    public <E extends BasicBitmapDrawable> void setTypedDrawable(E drawable) {
74        super.setImageDrawable(drawable);
75        unbindDrawable();
76        mDrawable = drawable;
77    }
78
79    private void unbindDrawable() {
80        unbindDrawable(PERMANENT);
81    }
82
83    private void unbindDrawable(boolean temporary) {
84        if (mDrawable != null) {
85            mDrawable.unbind(temporary);
86        }
87    }
88
89    @Override
90    public void setImageResource(final int resId) {
91        super.setImageResource(resId);
92        unbindDrawable();
93        mDrawable = null;
94    }
95
96    @Override
97    public void setImageURI(final Uri uri) {
98        super.setImageURI(uri);
99        unbindDrawable();
100        mDrawable = null;
101    }
102
103    @Override
104    public void setImageDrawable(final Drawable drawable) {
105        super.setImageDrawable(drawable);
106        unbindDrawable();
107        mDrawable = null;
108    }
109
110    @Override
111    public void setImageBitmap(final Bitmap bm) {
112        super.setImageBitmap(bm);
113        unbindDrawable();
114        mDrawable = null;
115    }
116
117    @Override
118    protected void onAttachedToWindow() {
119        super.onAttachedToWindow();
120        mAttachedToWindow = true;
121        if (mDrawable != null && mDrawable.getKey() == null
122              && mDrawable.getPreviousKey() != null) {
123            mDrawable.bind(mDrawable.getPreviousKey());
124        }
125    }
126
127    @Override
128    protected void onDetachedFromWindow() {
129        super.onDetachedFromWindow();
130        mAttachedToWindow = false;
131        if (HAS_TRANSIENT_STATE_SUPPORTED && !hasTransientState()) {
132            unbindDrawable(TEMPORARY);
133        }
134    }
135
136    @Override
137    public void setHasTransientState(boolean hasTransientState) {
138        super.setHasTransientState(hasTransientState);
139        if (!hasTransientState && !mAttachedToWindow) {
140            unbindDrawable(TEMPORARY);
141        }
142    }
143}
144