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  */
16 package com.android.messaging.datamodel.media;
17 
18 import android.content.res.Resources;
19 import android.graphics.Bitmap;
20 import android.graphics.BitmapFactory;
21 import android.graphics.drawable.Drawable;
22 
23 import com.android.messaging.util.Assert;
24 import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
25 
26 import java.util.Arrays;
27 import 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  */
42 public class EncodedImageResource extends ImageResource {
43     private final byte[] mImageBytes;
44 
EncodedImageResource(String key, byte[] imageBytes, int orientation)45     public EncodedImageResource(String key, byte[] imageBytes, int orientation) {
46         super(key, orientation);
47         mImageBytes = imageBytes;
48     }
49 
50     @Override
51     @DoesNotRunOnMainThread
getBitmap()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
getBytes()64     public byte[] getBytes() {
65         acquireLock();
66         try {
67             return Arrays.copyOf(mImageBytes, mImageBytes.length);
68         } finally {
69             releaseLock();
70         }
71     }
72 
73     @Override
reuseBitmap()74     public Bitmap reuseBitmap() {
75         return null;
76     }
77 
78     @Override
supportsBitmapReuse()79     public boolean supportsBitmapReuse() {
80         return false;
81     }
82 
83     @Override
getMediaSize()84     public int getMediaSize() {
85         return mImageBytes.length;
86     }
87 
88     @Override
close()89     protected void close() {
90     }
91 
92     @Override
getDrawable(Resources resources)93     public Drawable getDrawable(Resources resources) {
94         return null;
95     }
96 
97     @Override
isEncoded()98     boolean isEncoded() {
99         return true;
100     }
101 
102     @Override
getMediaDecodingRequest( final MediaRequest<? extends RefCountedMediaResource> originalRequest)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> {
DecodeImageRequest()115         public DecodeImageRequest() {
116             // Hold a ref onto the encoded resource before the request finishes.
117             addRef();
118         }
119 
120         @Override
getKey()121         public String getKey() {
122             return EncodedImageResource.this.getKey();
123         }
124 
125         @Override
126         @DoesNotRunOnMainThread
loadMediaBlocking(List<MediaRequest<ImageResource>> chainedTask)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
getMediaCache()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
getCacheId()148         public int getCacheId() {
149             return 0;
150         }
151 
152         @Override
getRequestType()153         public int getRequestType() {
154             return REQUEST_DECODE_MEDIA;
155         }
156 
157         @Override
getDescriptor()158         public MediaRequestDescriptor<ImageResource> getDescriptor() {
159             return null;
160         }
161     }
162 }
163