to top
StorageClient / src / com.example.android.storageclient /

StorageClientFragment.java

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
}