ImageRecord.java revision 40d9a0cd0a37d184ee737b2d7138a39e4292ce3e
1/*
2 * Copyright (C) 2010 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.apps.tag.record;
18
19import com.android.apps.tag.R;
20import com.google.common.base.Preconditions;
21
22import android.app.Activity;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.database.Cursor;
28import android.graphics.Bitmap;
29import android.graphics.BitmapFactory;
30import android.media.ThumbnailUtils;
31import android.net.Uri;
32import android.nfc.NdefRecord;
33import android.os.Parcel;
34import android.os.Parcelable;
35import android.provider.MediaStore;
36import android.provider.OpenableColumns;
37import android.view.LayoutInflater;
38import android.view.View;
39import android.view.ViewGroup;
40import android.widget.ImageView;
41import android.widget.TextView;
42import android.widget.Toast;
43
44import java.io.ByteArrayOutputStream;
45import java.util.List;
46
47/**
48 * A NdefRecord corresponding to an image type.
49 */
50public class ImageRecord extends ParsedNdefRecord {
51
52    public static final String RECORD_TYPE = "ImageRecord";
53
54    private final Bitmap mBitmap;
55
56    private ImageRecord(Bitmap bitmap) {
57        mBitmap = Preconditions.checkNotNull(bitmap);
58    }
59
60    @Override
61    public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent, int offset) {
62        ImageView image = (ImageView) inflater.inflate(R.layout.tag_image, parent, false);
63        image.setImageBitmap(mBitmap);
64        return image;
65    }
66
67    /**
68     * Returns a view in a list of record types for adding new records to a message.
69     */
70    public static View getAddView(Context context, LayoutInflater inflater, ViewGroup parent) {
71        ViewGroup root = (ViewGroup) inflater.inflate(
72                R.layout.tag_add_record_list_item, parent, false);
73
74        // Determine which Activity can retrieve images.
75        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
76        intent.addCategory(Intent.CATEGORY_OPENABLE);
77        intent.setType("image/*");
78
79        PackageManager pm = context.getPackageManager();
80        List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
81        if (activities.isEmpty()) {
82            return null;
83        }
84
85        ResolveInfo info = activities.get(0);
86        ((ImageView) root.findViewById(R.id.image)).setImageDrawable(info.loadIcon(pm));
87        ((TextView) root.findViewById(R.id.text)).setText(context.getString(R.string.photo));
88
89        root.setTag(new ImageRecordEditInfo(intent));
90        return root;
91    }
92
93    public static ImageRecord parse(NdefRecord record) {
94        MimeRecord underlyingRecord = MimeRecord.parse(record);
95        Preconditions.checkArgument(underlyingRecord.getMimeType().startsWith("image/"));
96
97        // Try to ensure it's a legal, valid image
98        byte[] content = underlyingRecord.getContent();
99        Bitmap bitmap = BitmapFactory.decodeByteArray(content, 0, content.length);
100        if (bitmap == null) {
101            throw new IllegalArgumentException("not a valid image file");
102        }
103        return new ImageRecord(bitmap);
104    }
105
106    public static boolean isImage(NdefRecord record) {
107        try {
108            parse(record);
109            return true;
110        } catch (IllegalArgumentException e) {
111            return false;
112        }
113    }
114
115    public static NdefRecord newImageRecord(Bitmap bitmap) {
116        ByteArrayOutputStream out = new ByteArrayOutputStream();
117        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
118        byte[] content = out.toByteArray();
119        return MimeRecord.newMimeRecord("image/jpeg", content);
120    }
121
122    private static class ImageRecordEditInfo extends RecordEditInfo {
123        private final Intent mIntent;
124        private String mCurrentPath;
125        private Bitmap mCachedValue;
126
127        public static final int MAX_IMAGE_SIZE = 128;
128
129        public ImageRecordEditInfo(Intent intent) {
130            super(RECORD_TYPE);
131            mIntent = intent;
132            mCurrentPath = "";
133        }
134
135        protected ImageRecordEditInfo(Parcel parcel) {
136            super(parcel);
137            mIntent = parcel.readParcelable(null);
138            mCurrentPath = parcel.readString();
139        }
140
141        @Override
142        public Intent getPickIntent() {
143            return mIntent;
144        }
145
146        @Override
147        public NdefRecord getValue() {
148            return ImageRecord.newImageRecord(getValueInternal());
149        }
150
151        private Bitmap getValueInternal() {
152            if (mCachedValue == null) {
153                Bitmap original = BitmapFactory.decodeFile(mCurrentPath);
154                int width = original.getWidth();
155                int height = original.getHeight();
156                int major = (width > height) ? width : height;
157                if (major > MAX_IMAGE_SIZE) {
158                    double scale = 1.0 * MAX_IMAGE_SIZE / major;
159                    width *= scale;
160                    height *= scale;
161                }
162                mCachedValue = ThumbnailUtils.extractThumbnail(original, width, height);
163            }
164            return mCachedValue;
165        }
166
167        @Override
168        public void handlePickResult(Context context, Intent data) {
169            Cursor cursor = null;
170            mCachedValue = null;
171
172            try {
173                String[] projection = { MediaStore.Images.Media.DATA, OpenableColumns.SIZE };
174                cursor = context.getContentResolver().query(
175                        data.getData(), projection, null, null, null);
176
177                if (cursor == null) {
178                    Toast.makeText(
179                            context,
180                            context.getResources().getString(R.string.bad_photo),
181                            Toast.LENGTH_LONG).show();
182                    throw new IllegalArgumentException("Selected image could not be loaded");
183                }
184
185                cursor.moveToFirst();
186                int size = cursor.getInt(1);
187                mCurrentPath = cursor.getString(0);
188
189                // TODO: enforce a size limit. May be tricky.
190
191            } finally {
192                if (cursor != null) {
193                    cursor.close();
194                }
195            }
196        }
197
198        @Override
199        public View getEditView(
200                Activity activity, LayoutInflater inflater,
201                ViewGroup parent, EditCallbacks callbacks) {
202            View result = buildEditView(
203                    activity, inflater, R.layout.tag_edit_image, parent, callbacks);
204            ((ImageView) result.findViewById(R.id.image)).setImageBitmap(getValueInternal());
205            result.setOnClickListener(this);
206            return result;
207        }
208
209        @Override
210        public void writeToParcel(Parcel out, int flags) {
211            super.writeToParcel(out, flags);
212            out.writeParcelable(mIntent, flags);
213            out.writeString(mCurrentPath);
214        }
215
216        @SuppressWarnings("unused")
217        public static final Parcelable.Creator<ImageRecordEditInfo> CREATOR =
218                new Parcelable.Creator<ImageRecordEditInfo>() {
219            @Override
220            public ImageRecordEditInfo createFromParcel(Parcel in) {
221                return new ImageRecordEditInfo(in);
222            }
223
224            @Override
225            public ImageRecordEditInfo[] newArray(int size) {
226                return new ImageRecordEditInfo[size];
227            }
228        };
229
230        @Override
231        public int describeContents() {
232            return 0;
233        }
234
235        @Override
236        public void onClick(View target) {
237            if (this == target.getTag()) {
238                mCallbacks.startPickForRecord(this, mIntent);
239            } else {
240                super.onClick(target);
241            }
242        }
243    }
244}
245