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();
    121             Bundle fragmentArguments = new Bundle();
    122             fragmentArguments.putParcelable("URI", uri);
    123             imageDialog.setArguments(fragmentArguments);
    124             imageDialog.show(fm, "image_dialog");
    125         }
    126         // END_INCLUDE (create_show_image_dialog)
    127     }
    128 
    129 
    130     /**
    131      * DialogFragment which displays an image, given a URI.
    132      */
    133     public static class ImageDialogFragment extends DialogFragment {
    134         private Dialog mDialog;
    135         private Uri mUri;
    136 
    137         @Override
    138         public void onCreate(Bundle savedInstanceState) {
    139             super.onCreate(savedInstanceState);
    140             mUri = getArguments().getParcelable("URI");
    141         }
    142 
    143         /** Create a Bitmap from the URI for that image and return it.
    144          *
    145          * @param uri the Uri for the image to return.
    146          */
    147         private Bitmap getBitmapFromUri(Uri uri) {
    148             ParcelFileDescriptor parcelFileDescriptor = null;
    149             try {
    150                 parcelFileDescriptor =
    151                         getActivity().getContentResolver().openFileDescriptor(uri, "r");
    152                 FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    153                 Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    154                 parcelFileDescriptor.close();
    155                 return image;
    156             } catch (Exception e) {
    157                 Log.e(TAG, "Failed to load image.", e);
    158                 return null;
    159             } finally {
    160                 try {
    161                     if (parcelFileDescriptor != null) {
    162                         parcelFileDescriptor.close();
    163                     }
    164                 } catch (IOException e) {
    165                     e.printStackTrace();
    166                     Log.e(TAG, "Error closing ParcelFile Descriptor");
    167                 }
    168             }
    169         }
    170 
    171         @Override
    172         public Dialog onCreateDialog(Bundle savedInstanceState) {
    173             mDialog = super.onCreateDialog(savedInstanceState);
    174             // To optimize for the "lightbox" style layout.  Since we're not actually displaying a
    175             // title, remove the bar along the top of the fragment where a dialog title would
    176             // normally go.
    177             mDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
    178             final ImageView imageView = new ImageView(getActivity());
    179             mDialog.setContentView(imageView);
    180 
    181             // BEGIN_INCLUDE (show_image)
    182             // Loading the image is going to require some sort of I/O, which must occur off the UI
    183             // thread.  Changing the ImageView to display the image must occur ON the UI thread.
    184             // The easiest way to divide up this labor is with an AsyncTask.  The doInBackground
    185             // method will run in a separate thread, but onPostExecute will run in the main
    186             // UI thread.
    187             AsyncTask<Uri, Void, Bitmap> imageLoadAsyncTask = new AsyncTask<Uri, Void, Bitmap>() {
    188                 @Override
    189                 protected Bitmap doInBackground(Uri... uris) {
    190                     dumpImageMetaData(uris[0]);
    191                     return getBitmapFromUri(uris[0]);
    192                 }
    193 
    194                 @Override
    195                 protected void onPostExecute(Bitmap bitmap) {
    196                     imageView.setImageBitmap(bitmap);
    197                 }
    198             };
    199             imageLoadAsyncTask.execute(mUri);
    200             // END_INCLUDE (show_image)
    201 
    202             return mDialog;
    203         }
    204 
    205         @Override
    206         public void onStop() {
    207             super.onStop();
    208             if (getDialog() != null) {
    209                 getDialog().dismiss();
    210             }
    211         }
    212 
    213         /**
    214          * Grabs metadata for a document specified by URI, logs it to the screen.
    215          *
    216          * @param uri The uri for the document whose metadata should be printed.
    217          */
    218         public void dumpImageMetaData(Uri uri) {
    219             // BEGIN_INCLUDE (dump_metadata)
    220 
    221             // The query, since it only applies to a single document, will only return one row.
    222             // no need to filter, sort, or select fields, since we want all fields for one
    223             // document.
    224             Cursor cursor = getActivity().getContentResolver()
    225                     .query(uri, null, null, null, null, null);
    226 
    227             try {
    228                 // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
    229                 // "if there's anything to look at, look at it" conditionals.
    230                 if (cursor != null && cursor.moveToFirst()) {
    231 
    232                     // Note it's called "Display Name".  This is provider-specific, and
    233                     // might not necessarily be the file name.
    234                     String displayName = cursor.getString(
    235                             cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
    236                     Log.i(TAG, "Display Name: " + displayName);
    237 
    238                     int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
    239                     // If the size is unknown, the value stored is null.  But since an int can't be
    240                     // null in java, the behavior is implementation-specific, which is just a fancy
    241                     // term for "unpredictable".  So as a rule, check if it's null before assigning
    242                     // to an int.  This will happen often:  The storage API allows for remote
    243                     // files, whose size might not be locally known.
    244                     String size = null;
    245                     if (!cursor.isNull(sizeIndex)) {
    246                         // Technically the column stores an int, but cursor.getString will do the
    247                         // conversion automatically.
    248                         size = cursor.getString(sizeIndex);
    249                     } else {
    250                         size = "Unknown";
    251                     }
    252                     Log.i(TAG, "Size: " + size);
    253                 }
    254             } finally {
    255                 if (cursor != null) {
    256                     cursor.close();
    257                 }
    258             }
    259             // END_INCLUDE (dump_metadata)
    260         }
    261     }
    262 }
    263