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