146616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei/*
246616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei * Copyright (C) 2013 The Android Open Source Project
346616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei *
446616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei * Licensed under the Apache License, Version 2.0 (the "License");
546616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei * you may not use this file except in compliance with the License.
646616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei * You may obtain a copy of the License at
746616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei *
846616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei *      http://www.apache.org/licenses/LICENSE-2.0
946616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei *
1046616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei * Unless required by applicable law or agreed to in writing, software
1146616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei * distributed under the License is distributed on an "AS IS" BASIS,
1246616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1346616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei * See the License for the specific language governing permissions and
1446616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei * limitations under the License.
1546616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei */
1646616414f6dabb6d57b0ac433a5fabe2abb8b5a9Mark Wei
17a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Weipackage com.android.bitmap.view;
18a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
19a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Weiimport android.content.Context;
20a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Weiimport android.graphics.Bitmap;
21a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Weiimport android.graphics.drawable.Drawable;
22a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Weiimport android.net.Uri;
23d7654f820a99358a7d2b2afb9b3a174690e213f6Mark Weiimport android.os.Build;
24a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Weiimport android.util.AttributeSet;
25a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Weiimport android.widget.ImageView;
26a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
27a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Weiimport com.android.bitmap.drawable.BasicBitmapDrawable;
28a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
29a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei/**
30a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei * A helpful ImageView replacement that can generally be used in lieu of ImageView.
319c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei * BitmapDrawableImageView has logic to unbind its BasicBitmapDrawable when it is detached from the
329c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei * window.
33a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev *
34a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev * If you are using this with RecyclerView,
35a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev * or any use-case where {@link android.view.View#onDetachedFromWindow} is
36a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev * not a good signal for unbind,
37a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev * makes sure you {@link #setShouldUnbindOnDetachFromWindow} to false.
38a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei */
399c6ac19d4a3d39b7c2992060957920118ff56a65Mark Weipublic class BitmapDrawableImageView extends ImageView {
401a340d4fca7babcb67d6cfa754f58c1a756dfca0Mark Wei    private static final boolean HAS_TRANSIENT_STATE_SUPPORTED =
41d7654f820a99358a7d2b2afb9b3a174690e213f6Mark Wei        Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
425b46c679794d2f4362e8064b0291c578835d75e0Mark Wei    private static final boolean TEMPORARY = true;
435b46c679794d2f4362e8064b0291c578835d75e0Mark Wei    private static final boolean PERMANENT = !TEMPORARY;
445b46c679794d2f4362e8064b0291c578835d75e0Mark Wei
45a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    private BasicBitmapDrawable mDrawable;
46a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev    private boolean mShouldUnbindOnDetachFromWindow = true;
479024423249c9b1779f523cc4fe757fda2d205844Mark Wei    private boolean mAttachedToWindow;
48a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
499c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    public BitmapDrawableImageView(final Context context) {
50a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei        this(context, null);
51a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    }
52a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
539c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    public BitmapDrawableImageView(final Context context, final AttributeSet attrs) {
54a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei        this(context, attrs, 0);
55a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    }
56a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
579c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    public BitmapDrawableImageView(final Context context, final AttributeSet attrs,
589c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei            final int defStyle) {
59a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei        super(context, attrs, defStyle);
60a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    }
61a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
62a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev  public boolean shouldUnbindOnDetachFromWindow() {
63a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev    return mShouldUnbindOnDetachFromWindow;
64a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev  }
65a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev
66a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev  public void setShouldUnbindOnDetachFromWindow(boolean shouldUnbindOnDetachFromWindow) {
67a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev        mShouldUnbindOnDetachFromWindow = shouldUnbindOnDetachFromWindow;
68a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev    }
69a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev
70a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    /**
7189e59f00d67791754e44e65413baa95f94056df4Mark Wei     * Get the source drawable for this BitmapDrawableImageView.
7289e59f00d67791754e44e65413baa95f94056df4Mark Wei     * @return The source drawable casted to the given type, or null if the type does not match.
739c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei     */
7489e59f00d67791754e44e65413baa95f94056df4Mark Wei    @SuppressWarnings("unchecked") // Cast to type parameter.
7589e59f00d67791754e44e65413baa95f94056df4Mark Wei    public <E extends BasicBitmapDrawable> E getTypedDrawable() {
7689e59f00d67791754e44e65413baa95f94056df4Mark Wei        try {
7789e59f00d67791754e44e65413baa95f94056df4Mark Wei            return (E) mDrawable;
7889e59f00d67791754e44e65413baa95f94056df4Mark Wei        } catch (Exception ignored) {
7989e59f00d67791754e44e65413baa95f94056df4Mark Wei            return null;
8089e59f00d67791754e44e65413baa95f94056df4Mark Wei        }
819c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    }
829c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei
839c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    /**
8489e59f00d67791754e44e65413baa95f94056df4Mark Wei     * Set the given drawable as the source for this BitmapDrawableImageView.
85a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei     * @param drawable The source drawable.
86a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei     */
8789e59f00d67791754e44e65413baa95f94056df4Mark Wei    public <E extends BasicBitmapDrawable> void setTypedDrawable(E drawable) {
88a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei        super.setImageDrawable(drawable);
89a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev        if (drawable != mDrawable) {
90a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev            unbindDrawable();
91a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev        }
92a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei        mDrawable = drawable;
93a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    }
94a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
95a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev    public void unbindDrawable() {
965b46c679794d2f4362e8064b0291c578835d75e0Mark Wei        unbindDrawable(PERMANENT);
975b46c679794d2f4362e8064b0291c578835d75e0Mark Wei    }
985b46c679794d2f4362e8064b0291c578835d75e0Mark Wei
995b46c679794d2f4362e8064b0291c578835d75e0Mark Wei    private void unbindDrawable(boolean temporary) {
1009c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        if (mDrawable != null) {
1015b46c679794d2f4362e8064b0291c578835d75e0Mark Wei            mDrawable.unbind(temporary);
1029c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        }
103a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    }
104a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
105a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    @Override
1069c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    public void setImageResource(final int resId) {
1079c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        super.setImageResource(resId);
1089c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        unbindDrawable();
109b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei        mDrawable = null;
110a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    }
111a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
112a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    @Override
1139c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    public void setImageURI(final Uri uri) {
1149c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        super.setImageURI(uri);
1159c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        unbindDrawable();
116b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei        mDrawable = null;
117a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    }
118a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
119a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    @Override
1209c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    public void setImageDrawable(final Drawable drawable) {
1219c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        super.setImageDrawable(drawable);
1229c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        unbindDrawable();
123b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei        mDrawable = null;
124a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    }
125a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
126a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    @Override
1279c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    public void setImageBitmap(final Bitmap bm) {
1289c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        super.setImageBitmap(bm);
1299c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        unbindDrawable();
130b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei        mDrawable = null;
131a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    }
132a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei
133a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    @Override
1349024423249c9b1779f523cc4fe757fda2d205844Mark Wei    protected void onAttachedToWindow() {
1359024423249c9b1779f523cc4fe757fda2d205844Mark Wei        super.onAttachedToWindow();
1369024423249c9b1779f523cc4fe757fda2d205844Mark Wei        mAttachedToWindow = true;
137d732a611bd661fc2ac3c801a545cfbba0f179484Mark Wei        if (mDrawable != null && mDrawable.getKey() == null
138a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev              && mDrawable.getPreviousKey() != null && mShouldUnbindOnDetachFromWindow) {
139ad97cd0e0753930ec68f189e6be1023dc0f62f75Mark Wei            mDrawable.bind(mDrawable.getPreviousKey());
1401a340d4fca7babcb67d6cfa754f58c1a756dfca0Mark Wei        }
1419024423249c9b1779f523cc4fe757fda2d205844Mark Wei    }
1429024423249c9b1779f523cc4fe757fda2d205844Mark Wei
1439024423249c9b1779f523cc4fe757fda2d205844Mark Wei    @Override
1449c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    protected void onDetachedFromWindow() {
1459c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        super.onDetachedFromWindow();
1469024423249c9b1779f523cc4fe757fda2d205844Mark Wei        mAttachedToWindow = false;
147a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev        if (HAS_TRANSIENT_STATE_SUPPORTED && !hasTransientState()
148a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev                && mShouldUnbindOnDetachFromWindow) {
1495b46c679794d2f4362e8064b0291c578835d75e0Mark Wei            unbindDrawable(TEMPORARY);
1509024423249c9b1779f523cc4fe757fda2d205844Mark Wei        }
1519024423249c9b1779f523cc4fe757fda2d205844Mark Wei    }
1529024423249c9b1779f523cc4fe757fda2d205844Mark Wei
1539024423249c9b1779f523cc4fe757fda2d205844Mark Wei    @Override
1549024423249c9b1779f523cc4fe757fda2d205844Mark Wei    public void setHasTransientState(boolean hasTransientState) {
1559024423249c9b1779f523cc4fe757fda2d205844Mark Wei        super.setHasTransientState(hasTransientState);
156a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev        if (!hasTransientState && !mAttachedToWindow && mShouldUnbindOnDetachFromWindow) {
1575b46c679794d2f4362e8064b0291c578835d75e0Mark Wei            unbindDrawable(TEMPORARY);
1589024423249c9b1779f523cc4fe757fda2d205844Mark Wei        }
159a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    }
160856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei
161856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    @Override
162856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    public void onRtlPropertiesChanged(int layoutDirection) {
163856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei        super.onRtlPropertiesChanged(layoutDirection);
164856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei        if (mDrawable != null) {
165856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei          mDrawable.setLayoutDirectionLocal(layoutDirection);
166856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei        }
167856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    }
168a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei}
169