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