content: /*
  * 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)
          }
      }
  }
  