Home | History | Annotate | Download | only in com.example.android.storageclient
      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 
     69         // BEGIN_INCLUDE (use_open_document_intent)
     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);
     84         // END_INCLUDE (use_open_document_intent)
     85     }
     86 
     87     @Override
     88     public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
     89         Log.i(TAG, "Received an \"Activity Result\"");
     90         // BEGIN_INCLUDE (parse_open_document_response)
     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             }
    105             // END_INCLUDE (parse_open_document_response)
    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) {
    115         // BEGIN_INCLUDE (create_show_image_dialog)
    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         }
    123         // END_INCLUDE (create_show_image_dialog)
    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) {
    132         // BEGIN_INCLUDE (dump_metadata)
    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         }
    170         // END_INCLUDE (dump_metadata)
    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 
    223             // BEGIN_INCLUDE (show_image)
    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);
    242             // END_INCLUDE (show_image)
    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 }
    256