| /* |
| * 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 android.support.v4.app.DialogFragment; |
| import android.support.v4.app.Fragment; |
| import android.support.v4.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(uri); |
| imageDialog.show(fm, "image_dialog"); |
| } |
| // END_INCLUDE (create_show_image_dialog) |
| } |
| |
| /** |
| * 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 { |
| cursor.close(); |
| } |
| // END_INCLUDE (dump_metadata) |
| } |
| |
| /** |
| * DialogFragment which displays an image, given a URI. |
| */ |
| private class ImageDialogFragment extends DialogFragment { |
| private Dialog mDialog; |
| private Uri mUri; |
| |
| public ImageDialogFragment(Uri uri) { |
| super(); |
| mUri = 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(); |
| } |
| } |
| } |
| } |