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 *
34 * If you are using this with RecyclerView,
35 * or any use-case where {@link android.view.View#onDetachedFromWindow} is
36 * not a good signal for unbind,
37 * makes sure you {@link #setShouldUnbindOnDetachFromWindow} to false.
38 */
39public class BitmapDrawableImageView extends ImageView {
40    private static final boolean HAS_TRANSIENT_STATE_SUPPORTED =
41        Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
42    private static final boolean TEMPORARY = true;
43    private static final boolean PERMANENT = !TEMPORARY;
44
45    private BasicBitmapDrawable mDrawable;
46    private boolean mShouldUnbindOnDetachFromWindow = true;
47    private boolean mAttachedToWindow;
48
49    public BitmapDrawableImageView(final Context context) {
50        this(context, null);
51    }
52
53    public BitmapDrawableImageView(final Context context, final AttributeSet attrs) {
54        this(context, attrs, 0);
55    }
56
57    public BitmapDrawableImageView(final Context context, final AttributeSet attrs,
58            final int defStyle) {
59        super(context, attrs, defStyle);
60    }
61
62  public boolean shouldUnbindOnDetachFromWindow() {
63    return mShouldUnbindOnDetachFromWindow;
64  }
65
66  public void setShouldUnbindOnDetachFromWindow(boolean shouldUnbindOnDetachFromWindow) {
67        mShouldUnbindOnDetachFromWindow = shouldUnbindOnDetachFromWindow;
68    }
69
70    /**
71     * Get the source drawable for this BitmapDrawableImageView.
72     * @return The source drawable casted to the given type, or null if the type does not match.
73     */
74    @SuppressWarnings("unchecked") // Cast to type parameter.
75    public <E extends BasicBitmapDrawable> E getTypedDrawable() {
76        try {
77            return (E) mDrawable;
78        } catch (Exception ignored) {
79            return null;
80        }
81    }
82
83    /**
84     * Set the given drawable as the source for this BitmapDrawableImageView.
85     * @param drawable The source drawable.
86     */
87    public <E extends BasicBitmapDrawable> void setTypedDrawable(E drawable) {
88        super.setImageDrawable(drawable);
89        if (drawable != mDrawable) {
90            unbindDrawable();
91        }
92        mDrawable = drawable;
93    }
94
95    public void unbindDrawable() {
96        unbindDrawable(PERMANENT);
97    }
98
99    private void unbindDrawable(boolean temporary) {
100        if (mDrawable != null) {
101            mDrawable.unbind(temporary);
102        }
103    }
104
105    @Override
106    public void setImageResource(final int resId) {
107        super.setImageResource(resId);
108        unbindDrawable();
109        mDrawable = null;
110    }
111
112    @Override
113    public void setImageURI(final Uri uri) {
114        super.setImageURI(uri);
115        unbindDrawable();
116        mDrawable = null;
117    }
118
119    @Override
120    public void setImageDrawable(final Drawable drawable) {
121        super.setImageDrawable(drawable);
122        unbindDrawable();
123        mDrawable = null;
124    }
125
126    @Override
127    public void setImageBitmap(final Bitmap bm) {
128        super.setImageBitmap(bm);
129        unbindDrawable();
130        mDrawable = null;
131    }
132
133    @Override
134    protected void onAttachedToWindow() {
135        super.onAttachedToWindow();
136        mAttachedToWindow = true;
137        if (mDrawable != null && mDrawable.getKey() == null
138              && mDrawable.getPreviousKey() != null && mShouldUnbindOnDetachFromWindow) {
139            mDrawable.bind(mDrawable.getPreviousKey());
140        }
141    }
142
143    @Override
144    protected void onDetachedFromWindow() {
145        super.onDetachedFromWindow();
146        mAttachedToWindow = false;
147        if (HAS_TRANSIENT_STATE_SUPPORTED && !hasTransientState()
148                && mShouldUnbindOnDetachFromWindow) {
149            unbindDrawable(TEMPORARY);
150        }
151    }
152
153    @Override
154    public void setHasTransientState(boolean hasTransientState) {
155        super.setHasTransientState(hasTransientState);
156        if (!hasTransientState && !mAttachedToWindow && mShouldUnbindOnDetachFromWindow) {
157            unbindDrawable(TEMPORARY);
158        }
159    }
160
161    @Override
162    public void onRtlPropertiesChanged(int layoutDirection) {
163        super.onRtlPropertiesChanged(layoutDirection);
164        if (mDrawable != null) {
165          mDrawable.setLayoutDirectionLocal(layoutDirection);
166        }
167    }
168}
169