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