1/*
2 * Copyright (C) 2015 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 */
16package com.android.messaging.datamodel.media;
17
18import android.content.res.Resources;
19import android.graphics.Bitmap;
20import android.graphics.BitmapFactory;
21import android.graphics.drawable.Drawable;
22
23import com.android.messaging.util.Assert;
24import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
25
26import java.util.Arrays;
27import java.util.List;
28
29/**
30 * A cache-facing image resource that's much more compact than the raw Bitmap objects stored in
31 * {@link com.android.messaging.datamodel.media.DecodedImageResource}.
32 *
33 * This resource is created from a regular Bitmap-based ImageResource before being pushed to
34 * {@link com.android.messaging.datamodel.media.MediaCache}, if the image request
35 * allows for resource encoding/compression.
36 *
37 * During resource retrieval on cache hit,
38 * {@link #getMediaDecodingRequest(MediaRequest)} is invoked to create a async
39 * decode task, which decodes the compressed byte array back to a regular image resource to
40 * be consumed by the UI.
41 */
42public class EncodedImageResource extends ImageResource {
43    private final byte[] mImageBytes;
44
45    public EncodedImageResource(String key, byte[] imageBytes, int orientation) {
46        super(key, orientation);
47        mImageBytes = imageBytes;
48    }
49
50    @Override
51    @DoesNotRunOnMainThread
52    public Bitmap getBitmap() {
53        acquireLock();
54        try {
55            // This should only be called during the decode request.
56            Assert.isNotMainThread();
57            return BitmapFactory.decodeByteArray(mImageBytes, 0, mImageBytes.length);
58        } finally {
59            releaseLock();
60        }
61    }
62
63    @Override
64    public byte[] getBytes() {
65        acquireLock();
66        try {
67            return Arrays.copyOf(mImageBytes, mImageBytes.length);
68        } finally {
69            releaseLock();
70        }
71    }
72
73    @Override
74    public Bitmap reuseBitmap() {
75        return null;
76    }
77
78    @Override
79    public boolean supportsBitmapReuse() {
80        return false;
81    }
82
83    @Override
84    public int getMediaSize() {
85        return mImageBytes.length;
86    }
87
88    @Override
89    protected void close() {
90    }
91
92    @Override
93    public Drawable getDrawable(Resources resources) {
94        return null;
95    }
96
97    @Override
98    boolean isEncoded() {
99        return true;
100    }
101
102    @Override
103    MediaRequest<? extends RefCountedMediaResource> getMediaDecodingRequest(
104            final MediaRequest<? extends RefCountedMediaResource> originalRequest) {
105        Assert.isTrue(isEncoded());
106        return new DecodeImageRequest();
107    }
108
109    /**
110     * A MediaRequest that decodes the encoded image resource. This class is chained to the
111     * original media request that requested the image, so it inherits the listener and
112     * properties such as binding.
113     */
114    private class DecodeImageRequest implements MediaRequest<ImageResource> {
115        public DecodeImageRequest() {
116            // Hold a ref onto the encoded resource before the request finishes.
117            addRef();
118        }
119
120        @Override
121        public String getKey() {
122            return EncodedImageResource.this.getKey();
123        }
124
125        @Override
126        @DoesNotRunOnMainThread
127        public ImageResource loadMediaBlocking(List<MediaRequest<ImageResource>> chainedTask)
128                throws Exception {
129            Assert.isNotMainThread();
130            acquireLock();
131            try {
132                final Bitmap decodedBitmap = BitmapFactory.decodeByteArray(mImageBytes, 0,
133                        mImageBytes.length);
134                return new DecodedImageResource(getKey(), decodedBitmap, getOrientation());
135            } finally {
136                releaseLock();
137                release();
138            }
139        }
140
141        @Override
142        public MediaCache<ImageResource> getMediaCache() {
143            // Decoded resource is non-cachable, it's for UI consumption only (for now at least)
144            return null;
145        }
146
147        @Override
148        public int getCacheId() {
149            return 0;
150        }
151
152        @Override
153        public int getRequestType() {
154            return REQUEST_DECODE_MEDIA;
155        }
156
157        @Override
158        public MediaRequestDescriptor<ImageResource> getDescriptor() {
159            return null;
160        }
161    }
162}
163