Home | History | Annotate | Download | only in scopeddirectoryaccess
      1 /*
      2 * Copyright 2016 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.scopeddirectoryaccess;
     18 
     19 import android.app.Activity;
     20 import android.content.ContentResolver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.database.Cursor;
     24 import android.net.Uri;
     25 import android.os.Bundle;
     26 import android.os.Environment;
     27 import android.os.storage.StorageManager;
     28 import android.os.storage.StorageVolume;
     29 import android.provider.DocumentsContract;
     30 import android.support.v4.app.Fragment;
     31 import android.support.v7.widget.LinearLayoutManager;
     32 import android.support.v7.widget.RecyclerView;
     33 import android.view.LayoutInflater;
     34 import android.view.View;
     35 import android.view.ViewGroup;
     36 import android.widget.ArrayAdapter;
     37 import android.widget.Button;
     38 import android.widget.LinearLayout;
     39 import android.widget.Spinner;
     40 import android.widget.TextView;
     41 
     42 import java.util.ArrayList;
     43 import java.util.List;
     44 
     45 /**
     46  * Fragment that displays the directory contents.
     47  */
     48 public class ScopedDirectoryAccessFragment extends Fragment {
     49 
     50     private static final String DIRECTORY_ENTRIES_KEY = "directory_entries";
     51     private static final String SELECTED_DIRECTORY_KEY = "selected_directory";
     52     private static final int OPEN_DIRECTORY_REQUEST_CODE = 1;
     53 
     54     private static final String[] DIRECTORY_SELECTION = new String[]{
     55             DocumentsContract.Document.COLUMN_DISPLAY_NAME,
     56             DocumentsContract.Document.COLUMN_MIME_TYPE,
     57             DocumentsContract.Document.COLUMN_DOCUMENT_ID,
     58     };
     59 
     60     private Activity mActivity;
     61     private StorageManager mStorageManager;
     62     private TextView mCurrentDirectoryTextView;
     63     private TextView mNothingInDirectoryTextView;
     64     private TextView mPrimaryVolumeNameTextView;
     65     private Spinner mDirectoriesSpinner;
     66     private DirectoryEntryAdapter mAdapter;
     67     private ArrayList<DirectoryEntry> mDirectoryEntries;
     68 
     69     public static ScopedDirectoryAccessFragment newInstance() {
     70         ScopedDirectoryAccessFragment fragment = new ScopedDirectoryAccessFragment();
     71         return fragment;
     72     }
     73 
     74     public ScopedDirectoryAccessFragment() {
     75         // Required empty public constructor
     76     }
     77 
     78     @Override
     79     public void onAttach(Context context) {
     80         super.onAttach(context);
     81         mActivity = getActivity();
     82         mStorageManager = mActivity.getSystemService(StorageManager.class);
     83     }
     84 
     85     @Override
     86     public void onActivityResult(int requestCode, int resultCode, Intent data) {
     87         super.onActivityResult(requestCode, resultCode, data);
     88         if (requestCode == OPEN_DIRECTORY_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
     89             getActivity().getContentResolver().takePersistableUriPermission(data.getData(),
     90                     Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
     91             updateDirectoryEntries(data.getData());
     92         }
     93     }
     94 
     95     @Override
     96     public View onCreateView(LayoutInflater inflater, ViewGroup container,
     97             Bundle savedInstanceState) {
     98         return inflater.inflate(R.layout.fragment_scoped_directory_access, container, false);
     99     }
    100 
    101     @Override
    102     public void onViewCreated(final View rootView, Bundle savedInstanceState) {
    103         super.onViewCreated(rootView, savedInstanceState);
    104 
    105         mCurrentDirectoryTextView = (TextView) rootView
    106                 .findViewById(R.id.textview_current_directory);
    107         mNothingInDirectoryTextView = (TextView) rootView
    108                 .findViewById(R.id.textview_nothing_in_directory);
    109         mPrimaryVolumeNameTextView = (TextView) rootView
    110                 .findViewById(R.id.textview_primary_volume_name);
    111 
    112         // Set onClickListener for the primary volume
    113         Button openPictureButton = (Button) rootView
    114                 .findViewById(R.id.button_open_directory_primary_volume);
    115         openPictureButton.setOnClickListener(new View.OnClickListener() {
    116             @Override
    117             public void onClick(View view) {
    118                 String selected = mDirectoriesSpinner.getSelectedItem().toString();
    119                 String directoryName = getDirectoryName(selected);
    120                 StorageVolume storageVolume = mStorageManager.getPrimaryStorageVolume();
    121                 Intent intent = storageVolume.createAccessIntent(directoryName);
    122                 startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE);
    123             }
    124         });
    125 
    126         // Set onClickListener for the external volumes if exists
    127         List<StorageVolume> storageVolumes = mStorageManager.getStorageVolumes();
    128         LinearLayout containerVolumes = (LinearLayout) mActivity
    129                 .findViewById(R.id.container_volumes);
    130         for (final StorageVolume volume : storageVolumes) {
    131             String volumeDescription = volume.getDescription(mActivity);
    132             if (volume.isPrimary()) {
    133                 // Primary volume area is already added...
    134                 if (volumeDescription != null) {
    135                     // ...but with a default name: set it to the real name when available.
    136                     mPrimaryVolumeNameTextView.setText(volumeDescription);
    137                 }
    138                 continue;
    139             }
    140             LinearLayout volumeArea = (LinearLayout) mActivity.getLayoutInflater()
    141                     .inflate(R.layout.volume_entry, containerVolumes);
    142             TextView volumeName = (TextView) volumeArea.findViewById(R.id.textview_volume_name);
    143             volumeName.setText(volumeDescription);
    144             Button button = (Button) volumeArea.findViewById(R.id.button_open_directory);
    145             button.setOnClickListener(new View.OnClickListener() {
    146                 @Override
    147                 public void onClick(View view) {
    148                     String selected = mDirectoriesSpinner.getSelectedItem().toString();
    149                     String directoryName = getDirectoryName(selected);
    150                     Intent intent = volume.createAccessIntent(directoryName);
    151                     startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE);
    152                 }
    153             });
    154         }
    155         RecyclerView recyclerView = (RecyclerView) rootView
    156                 .findViewById(R.id.recyclerview_directory_entries);
    157         if (savedInstanceState != null) {
    158             mDirectoryEntries = savedInstanceState.getParcelableArrayList(DIRECTORY_ENTRIES_KEY);
    159             mCurrentDirectoryTextView.setText(savedInstanceState.getString(SELECTED_DIRECTORY_KEY));
    160             mAdapter = new DirectoryEntryAdapter(mDirectoryEntries);
    161             if (mAdapter.getItemCount() == 0) {
    162                 mNothingInDirectoryTextView.setVisibility(View.VISIBLE);
    163             }
    164         } else {
    165             mDirectoryEntries = new ArrayList<>();
    166             mAdapter = new DirectoryEntryAdapter();
    167         }
    168         recyclerView.setAdapter(mAdapter);
    169         recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    170         mDirectoriesSpinner = (Spinner) rootView.findViewById(R.id.spinner_directories);
    171         ArrayAdapter<CharSequence> directoriesAdapter = ArrayAdapter
    172                 .createFromResource(getActivity(),
    173                         R.array.directories, android.R.layout.simple_spinner_item);
    174         directoriesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    175         mDirectoriesSpinner.setAdapter(directoriesAdapter);
    176     }
    177 
    178     @Override
    179     public void onSaveInstanceState(Bundle outState) {
    180         super.onSaveInstanceState(outState);
    181         outState.putString(SELECTED_DIRECTORY_KEY, mCurrentDirectoryTextView.getText().toString());
    182         outState.putParcelableArrayList(DIRECTORY_ENTRIES_KEY, mDirectoryEntries);
    183     }
    184 
    185     private void updateDirectoryEntries(Uri uri) {
    186         mDirectoryEntries.clear();
    187         ContentResolver contentResolver = getActivity().getContentResolver();
    188         Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri,
    189                 DocumentsContract.getTreeDocumentId(uri));
    190         Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri,
    191                 DocumentsContract.getTreeDocumentId(uri));
    192 
    193         try (Cursor docCursor = contentResolver
    194                 .query(docUri, DIRECTORY_SELECTION, null, null, null)) {
    195             while (docCursor != null && docCursor.moveToNext()) {
    196                 mCurrentDirectoryTextView.setText(docCursor.getString(docCursor.getColumnIndex(
    197                         DocumentsContract.Document.COLUMN_DISPLAY_NAME)));
    198             }
    199         }
    200 
    201         try (Cursor childCursor = contentResolver
    202                 .query(childrenUri, DIRECTORY_SELECTION, null, null, null)) {
    203             while (childCursor != null && childCursor.moveToNext()) {
    204                 DirectoryEntry entry = new DirectoryEntry();
    205                 entry.fileName = childCursor.getString(childCursor.getColumnIndex(
    206                         DocumentsContract.Document.COLUMN_DISPLAY_NAME));
    207                 entry.mimeType = childCursor.getString(childCursor.getColumnIndex(
    208                         DocumentsContract.Document.COLUMN_MIME_TYPE));
    209                 mDirectoryEntries.add(entry);
    210             }
    211 
    212             if (mDirectoryEntries.isEmpty()) {
    213                 mNothingInDirectoryTextView.setVisibility(View.VISIBLE);
    214             } else {
    215                 mNothingInDirectoryTextView.setVisibility(View.GONE);
    216             }
    217             mAdapter.setDirectoryEntries(mDirectoryEntries);
    218             mAdapter.notifyDataSetChanged();
    219         }
    220     }
    221 
    222     private String getDirectoryName(String name) {
    223         switch (name) {
    224             case "ALARMS":
    225                 return Environment.DIRECTORY_ALARMS;
    226             case "DCIM":
    227                 return Environment.DIRECTORY_DCIM;
    228             case "DOCUMENTS":
    229                 return Environment.DIRECTORY_DOCUMENTS;
    230             case "DOWNLOADS":
    231                 return Environment.DIRECTORY_DOWNLOADS;
    232             case "MOVIES":
    233                 return Environment.DIRECTORY_MOVIES;
    234             case "MUSIC":
    235                 return Environment.DIRECTORY_MUSIC;
    236             case "NOTIFICATIONS":
    237                 return Environment.DIRECTORY_NOTIFICATIONS;
    238             case "PICTURES":
    239                 return Environment.DIRECTORY_PICTURES;
    240             case "PODCASTS":
    241                 return Environment.DIRECTORY_PODCASTS;
    242             case "RINGTONES":
    243                 return Environment.DIRECTORY_RINGTONES;
    244             default:
    245                 throw new IllegalArgumentException("Invalid directory representation: " + name);
    246         }
    247     }
    248 }
    249