1
/*
2
* Copyright (C) 2012 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
17
package com.example.android.storageclient;
18
19
import android.app.Activity;
20
import android.app.Dialog;
21
import android.content.Intent;
22
import android.database.Cursor;
23
import android.graphics.Bitmap;
24
import android.graphics.BitmapFactory;
25
import android.net.Uri;
26
import android.os.AsyncTask;
27
import android.os.Bundle;
28
import android.os.ParcelFileDescriptor;
29
import android.provider.OpenableColumns;
30
import android.support.v4.app.DialogFragment;
31
import android.support.v4.app.Fragment;
32
import android.support.v4.app.FragmentManager;
33
import android.view.MenuItem;
34
import android.view.Window;
35
import android.widget.ImageView;
36
37
import com.example.android.common.logger.Log;
38
39
import java.io.FileDescriptor;
40
import java.io.IOException;
41
42
public class StorageClientFragment extends Fragment {
43
44
// A request code's purpose is to match the result of a "startActivityForResult" with
45
// the type of the original request. Choose any value.
46
private static final int READ_REQUEST_CODE = 1337;
47
48
public static final String TAG = "StorageClientFragment";
49
50
@Override
51
public void onCreate(Bundle savedInstanceState) {
52
super.onCreate(savedInstanceState);
53
setHasOptionsMenu(true);
54
}
55
56
@Override
57
public boolean onOptionsItemSelected(MenuItem item) {
58
if (item.getItemId() == R.id.sample_action) {
59
performFileSearch();
60
}
61
return true;
62
}
63
64
/**
65
* Fires an intent to spin up the "file chooser" UI and select an image.
66
*/
67
public void performFileSearch() {
68
70
// ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file browser.
71
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
72
73
// Filter to only show results that can be "opened", such as a file (as opposed to a list
74
// of contacts or timezones)
75
intent.addCategory(Intent.CATEGORY_OPENABLE);
76
77
// Filter to show only images, using the image MIME data type.
78
// If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
79
// To search for all documents available via installed storage providers, it would be
80
// "*/*".
81
intent.setType("image/*");
82
83
startActivityForResult(intent, READ_REQUEST_CODE);
85
}
86
87
@Override
88
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
89
Log.i(TAG, "Received an \"Activity Result\"");
91
// The ACTION_OPEN_DOCUMENT intent was sent with the request code READ_REQUEST_CODE.
92
// If the request code seen here doesn't match, it's the response to some other intent,
93
// and the below code shouldn't run at all.
94
95
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
96
// The document selected by the user won't be returned in the intent.
97
// Instead, a URI to that document will be contained in the return intent
98
// provided to this method as a parameter. Pull that uri using "resultData.getData()"
99
Uri uri = null;
100
if (resultData != null) {
101
uri = resultData.getData();
102
Log.i(TAG, "Uri: " + uri.toString());
103
showImage(uri);
104
}
106
}
107
}
108
109
/**
110
* Given the URI of an image, shows it on the screen using a DialogFragment.
111
*
112
* @param uri the Uri of the image to display.
113
*/
114
public void showImage(Uri uri) {
116
if (uri != null) {
117
// Since the URI is to an image, create and show a DialogFragment to display the
118
// image to the user.
119
FragmentManager fm = getActivity().getSupportFragmentManager();
120
ImageDialogFragment imageDialog = new ImageDialogFragment(uri);
121
imageDialog.show(fm, "image_dialog");
122
}
124
}
125
126
/**
127
* Grabs metadata for a document specified by URI, logs it to the screen.
128
*
129
* @param uri The uri for the document whose metadata should be printed.
130
*/
131
public void dumpImageMetaData(Uri uri) {
133
134
// The query, since it only applies to a single document, will only return one row.
135
// no need to filter, sort, or select fields, since we want all fields for one
136
// document.
137
Cursor cursor = getActivity().getContentResolver()
138
.query(uri, null, null, null, null, null);
139
140
try {
141
// moveToFirst() returns false if the cursor has 0 rows. Very handy for
142
// "if there's anything to look at, look at it" conditionals.
143
if (cursor != null && cursor.moveToFirst()) {
144
145
// Note it's called "Display Name". This is provider-specific, and
146
// might not necessarily be the file name.
147
String displayName = cursor.getString(
148
cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
149
Log.i(TAG, "Display Name: " + displayName);
150
151
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
152
// If the size is unknown, the value stored is null. But since an int can't be
153
// null in java, the behavior is implementation-specific, which is just a fancy
154
// term for "unpredictable". So as a rule, check if it's null before assigning
155
// to an int. This will happen often: The storage API allows for remote
156
// files, whose size might not be locally known.
157
String size = null;
158
if (!cursor.isNull(sizeIndex)) {
159
// Technically the column stores an int, but cursor.getString will do the
160
// conversion automatically.
161
size = cursor.getString(sizeIndex);
162
} else {
163
size = "Unknown";
164
}
165
Log.i(TAG, "Size: " + size);
166
}
167
} finally {
168
cursor.close();
169
}
171
}
172
173
/**
174
* DialogFragment which displays an image, given a URI.
175
*/
176
private class ImageDialogFragment extends DialogFragment {
177
private Dialog mDialog;
178
private Uri mUri;
179
180
public ImageDialogFragment(Uri uri) {
181
super();
182
mUri = uri;
183
}
184
185
/** Create a Bitmap from the URI for that image and return it.
186
*
187
* @param uri the Uri for the image to return.
188
*/
189
private Bitmap getBitmapFromUri(Uri uri) {
190
ParcelFileDescriptor parcelFileDescriptor = null;
191
try {
192
parcelFileDescriptor =
193
getActivity().getContentResolver().openFileDescriptor(uri, "r");
194
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
195
Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
196
parcelFileDescriptor.close();
197
return image;
198
} catch (Exception e) {
199
Log.e(TAG, "Failed to load image.", e);
200
return null;
201
} finally {
202
try {
203
if (parcelFileDescriptor != null) {
204
parcelFileDescriptor.close();
205
}
206
} catch (IOException e) {
207
e.printStackTrace();
208
Log.e(TAG, "Error closing ParcelFile Descriptor");
209
}
210
}
211
}
212
213
@Override
214
public Dialog onCreateDialog(Bundle savedInstanceState) {
215
mDialog = super.onCreateDialog(savedInstanceState);
216
// To optimize for the "lightbox" style layout. Since we're not actually displaying a
217
// title, remove the bar along the top of the fragment where a dialog title would
218
// normally go.
219
mDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
220
final ImageView imageView = new ImageView(getActivity());
221
mDialog.setContentView(imageView);
222
224
// Loading the image is going to require some sort of I/O, which must occur off the UI
225
// thread. Changing the ImageView to display the image must occur ON the UI thread.
226
// The easiest way to divide up this labor is with an AsyncTask. The doInBackground
227
// method will run in a separate thread, but onPostExecute will run in the main
228
// UI thread.
229
AsyncTask<Uri, Void, Bitmap> imageLoadAsyncTask = new AsyncTask<Uri, Void, Bitmap>() {
230
@Override
231
protected Bitmap doInBackground(Uri... uris) {
232
dumpImageMetaData(uris[0]);
233
return getBitmapFromUri(uris[0]);
234
}
235
236
@Override
237
protected void onPostExecute(Bitmap bitmap) {
238
imageView.setImageBitmap(bitmap);
239
}
240
};
241
imageLoadAsyncTask.execute(mUri);
243
244
return mDialog;
245
}
246
247
@Override
248
public void onStop() {
249
super.onStop();
250
if (getDialog() != null) {
251
getDialog().dismiss();
252
}
253
}
254
}
255
}