DirectoryFragment.java revision ae9b51bfa313c51a31af30875a71255d7b6d2e61
19e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey/*
29e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey * Copyright (C) 2013 The Android Open Source Project
39e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey *
49e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
59e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey * you may not use this file except in compliance with the License.
69e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey * You may obtain a copy of the License at
79e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey *
89e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
99e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey *
109e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
119e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
129e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey * See the License for the specific language governing permissions and
149e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey * limitations under the License.
159e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey */
169e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
179e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeypackage com.android.documentsui;
189e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
199d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkeyimport static com.android.documentsui.DocumentsActivity.TAG;
204eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkeyimport static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_MANAGE;
214eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkeyimport static com.android.documentsui.DocumentsActivity.DisplayState.MODE_GRID;
224eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkeyimport static com.android.documentsui.DocumentsActivity.DisplayState.MODE_LIST;
23aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_DISPLAY_NAME;
24aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkeyimport static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_LAST_MODIFIED;
254eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkeyimport static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_SIZE;
269d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
2754e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkeyimport android.app.Fragment;
289e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.app.FragmentManager;
299e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.app.FragmentTransaction;
309e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.app.LoaderManager.LoaderCallbacks;
314eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkeyimport android.content.ContentResolver;
329e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.content.Context;
334eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkeyimport android.content.Intent;
349e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.content.Loader;
359d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkeyimport android.graphics.Bitmap;
369d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkeyimport android.graphics.Point;
379e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.net.Uri;
389d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkeyimport android.os.AsyncTask;
399e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.os.Bundle;
409e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.provider.DocumentsContract;
4154e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkeyimport android.text.format.DateUtils;
42b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkeyimport android.text.format.Formatter;
439d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkeyimport android.text.format.Time;
449d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkeyimport android.util.Log;
45be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkeyimport android.util.SparseBooleanArray;
46be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkeyimport android.view.ActionMode;
479e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.view.LayoutInflater;
4854e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkeyimport android.view.Menu;
4954e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkeyimport android.view.MenuItem;
509e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.view.View;
519e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.view.ViewGroup;
52be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkeyimport android.widget.AbsListView;
53be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkeyimport android.widget.AbsListView.MultiChoiceModeListener;
5454e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkeyimport android.widget.AdapterView;
5554e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkeyimport android.widget.AdapterView.OnItemClickListener;
56ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkeyimport android.widget.BaseAdapter;
5754e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkeyimport android.widget.GridView;
589e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.widget.ImageView;
599e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.widget.ListView;
609e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkeyimport android.widget.TextView;
614eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkeyimport android.widget.Toast;
629e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
631482789374fb8da3abea8f6f4f272a67205b95dbJeff Sharkeyimport com.android.documentsui.DocumentsActivity.DisplayState;
64ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkeyimport com.android.documentsui.model.DocumentInfo;
65ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkeyimport com.android.internal.util.Predicate;
66be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkeyimport com.google.android.collect.Lists;
67be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey
68be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkeyimport java.util.ArrayList;
69ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkeyimport java.util.Comparator;
70ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkeyimport java.util.List;
71b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkeyimport java.util.concurrent.atomic.AtomicInteger;
7254e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
7354e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey/**
7454e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey * Display the documents inside a single directory.
7554e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey */
7654e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkeypublic class DirectoryFragment extends Fragment {
7754e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
789fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey    private View mEmptyView;
7954e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey    private ListView mListView;
8054e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey    private GridView mGridView;
8154e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
82be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey    private AbsListView mCurrentView;
83be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey
84ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    private Predicate<DocumentInfo> mFilter;
859fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey
86ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey    public static final int TYPE_NORMAL = 1;
87ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey    public static final int TYPE_SEARCH = 2;
88ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey    public static final int TYPE_RECENT_OPEN = 3;
89dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey
90dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey    private int mType = TYPE_NORMAL;
91dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey
929d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey    private Point mThumbSize;
939d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
949e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    private DocumentsAdapter mAdapter;
95b448660a227f93d7bd5c5d019a21745e67c2f54dJeff Sharkey    private LoaderCallbacks<DirectoryResult> mCallbacks;
969e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
97b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    private static final String EXTRA_TYPE = "type";
98dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey    private static final String EXTRA_URI = "uri";
999e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
100b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    private static AtomicInteger sLoaderId = new AtomicInteger(4000);
101b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey
102b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    private final int mLoaderId = sLoaderId.incrementAndGet();
103b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey
104b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    public static void showNormal(FragmentManager fm, Uri uri) {
105b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        show(fm, TYPE_NORMAL, uri);
106b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    }
107b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey
108b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    public static void showSearch(FragmentManager fm, Uri uri, String query) {
109ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        final Uri searchUri = DocumentsContract.buildSearchDocumentsUri(
110ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                uri.getAuthority(), DocumentsContract.getDocumentId(uri), query);
111b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        show(fm, TYPE_SEARCH, searchUri);
112b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    }
113b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey
114b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    public static void showRecentsOpen(FragmentManager fm) {
115b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        show(fm, TYPE_RECENT_OPEN, null);
116b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    }
1179e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
118b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    private static void show(FragmentManager fm, int type, Uri uri) {
1199e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        final Bundle args = new Bundle();
120b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        args.putInt(EXTRA_TYPE, type);
121dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey        args.putParcelable(EXTRA_URI, uri);
1229e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
1239e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        final DirectoryFragment fragment = new DirectoryFragment();
1249e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        fragment.setArguments(args);
1259e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
1269e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        final FragmentTransaction ft = fm.beginTransaction();
12766516697a29cf00d93893a1011d3befc7c1ee37fJeff Sharkey        ft.replace(R.id.container_directory, fragment);
1289e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        ft.commitAllowingStateLoss();
1299e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    }
1309e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
131ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey    public static DirectoryFragment get(FragmentManager fm) {
132ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        // TODO: deal with multiple directories shown at once
13366516697a29cf00d93893a1011d3befc7c1ee37fJeff Sharkey        return (DirectoryFragment) fm.findFragmentById(R.id.container_directory);
134ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey    }
135ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey
1369e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    @Override
1379e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    public View onCreateView(
1389e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
1399e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        final Context context = inflater.getContext();
1409e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
14154e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        final View view = inflater.inflate(R.layout.fragment_directory, container, false);
14254e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
1439fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey        mEmptyView = view.findViewById(android.R.id.empty);
1449fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey
14554e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        mListView = (ListView) view.findViewById(R.id.list);
14654e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        mListView.setOnItemClickListener(mItemListener);
147be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        mListView.setMultiChoiceModeListener(mMultiListener);
14854e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
14954e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        mGridView = (GridView) view.findViewById(R.id.grid);
15054e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        mGridView.setOnItemClickListener(mItemListener);
151be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        mGridView.setMultiChoiceModeListener(mMultiListener);
15254e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
153ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        mAdapter = new DocumentsAdapter();
15454e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
155dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey        final Uri uri = getArguments().getParcelable(EXTRA_URI);
156b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        mType = getArguments().getInt(EXTRA_TYPE);
1579e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
158b448660a227f93d7bd5c5d019a21745e67c2f54dJeff Sharkey        mCallbacks = new LoaderCallbacks<DirectoryResult>() {
1599e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey            @Override
160b448660a227f93d7bd5c5d019a21745e67c2f54dJeff Sharkey            public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
1611482789374fb8da3abea8f6f4f272a67205b95dbJeff Sharkey                final DisplayState state = getDisplayState(DirectoryFragment.this);
1629fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey                mFilter = new MimePredicate(state.acceptMimes);
1631482789374fb8da3abea8f6f4f272a67205b95dbJeff Sharkey
16454ca29a5b94c2edf461c5433825d4ae17469fd7cJeff Sharkey                Uri contentsUri;
165dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey                if (mType == TYPE_NORMAL) {
166ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    contentsUri = DocumentsContract.buildChildDocumentsUri(
167ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                            uri.getAuthority(), DocumentsContract.getDocumentId(uri));
168b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                } else if (mType == TYPE_RECENT_OPEN) {
169b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                    contentsUri = RecentsProvider.buildRecentOpen();
170bc2971d2499f5bf5534ced7296ece630ca902738Jeff Sharkey                } else {
171dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey                    contentsUri = uri;
172bc2971d2499f5bf5534ced7296ece630ca902738Jeff Sharkey                }
173bc2971d2499f5bf5534ced7296ece630ca902738Jeff Sharkey
174ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                final Comparator<DocumentInfo> sortOrder;
175aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                if (state.sortOrder == SORT_ORDER_LAST_MODIFIED || mType == TYPE_RECENT_OPEN) {
176ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    sortOrder = new DocumentInfo.LastModifiedComparator();
177aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                } else if (state.sortOrder == SORT_ORDER_DISPLAY_NAME) {
178ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    sortOrder = new DocumentInfo.DisplayNameComparator();
1794eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                } else if (state.sortOrder == SORT_ORDER_SIZE) {
180ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    sortOrder = new DocumentInfo.SizeComparator();
181ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey                } else {
182ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey                    throw new IllegalArgumentException("Unknown sort order " + state.sortOrder);
183ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey                }
184ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey
1859fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey                return new DirectoryLoader(context, contentsUri, mType, null, sortOrder);
1869e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey            }
1879e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
1889e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey            @Override
189b448660a227f93d7bd5c5d019a21745e67c2f54dJeff Sharkey            public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
190b448660a227f93d7bd5c5d019a21745e67c2f54dJeff Sharkey                mAdapter.swapDocuments(result.contents);
1919e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey            }
1929e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
1939e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey            @Override
194b448660a227f93d7bd5c5d019a21745e67c2f54dJeff Sharkey            public void onLoaderReset(Loader<DirectoryResult> loader) {
195ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey                mAdapter.swapDocuments(null);
1969e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey            }
1979e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        };
1989e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
199b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        updateDisplayState();
200b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey
20154e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        return view;
2029e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    }
2039e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
2049e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    @Override
2059e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    public void onStart() {
2069e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        super.onStart();
207b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        getLoaderManager().restartLoader(mLoaderId, getArguments(), mCallbacks);
2089e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    }
2099e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
2109e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    @Override
2119e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    public void onStop() {
2129e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        super.onStop();
213b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        getLoaderManager().destroyLoader(mLoaderId);
21454e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey    }
2159e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
216b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey    public void updateDisplayState() {
2171482789374fb8da3abea8f6f4f272a67205b95dbJeff Sharkey        final DisplayState state = getDisplayState(this);
21854e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
2199fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey        // TODO: avoid kicking loader when nothing changed
220b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        getLoaderManager().restartLoader(mLoaderId, getArguments(), mCallbacks);
221b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        mListView.smoothScrollToPosition(0);
222b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey        mGridView.smoothScrollToPosition(0);
22354e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
2244eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        mListView.setVisibility(state.mode == MODE_LIST ? View.VISIBLE : View.GONE);
2254eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        mGridView.setVisibility(state.mode == MODE_GRID ? View.VISIBLE : View.GONE);
22654e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
227be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        final int choiceMode;
2281482789374fb8da3abea8f6f4f272a67205b95dbJeff Sharkey        if (state.allowMultiple) {
229be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
230be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        } else {
231be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            choiceMode = ListView.CHOICE_MODE_NONE;
232be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        }
233be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey
2349d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        final int thumbSize;
2354eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        if (state.mode == MODE_GRID) {
2369d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width);
23754e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey            mListView.setAdapter(null);
238be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
23954e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey            mGridView.setAdapter(mAdapter);
240be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            mGridView.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.grid_width));
24154e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey            mGridView.setNumColumns(GridView.AUTO_FIT);
242be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            mGridView.setChoiceMode(choiceMode);
243be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            mCurrentView = mGridView;
2444eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        } else if (state.mode == MODE_LIST) {
2459d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            thumbSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
24654e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey            mGridView.setAdapter(null);
247be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
24854e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey            mListView.setAdapter(mAdapter);
249be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            mListView.setChoiceMode(choiceMode);
250be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            mCurrentView = mListView;
2511482789374fb8da3abea8f6f4f272a67205b95dbJeff Sharkey        } else {
2521482789374fb8da3abea8f6f4f272a67205b95dbJeff Sharkey            throw new IllegalStateException();
25354e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        }
2549d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
2559d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        mThumbSize = new Point(thumbSize, thumbSize);
25654e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey    }
25754e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
25854e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey    private OnItemClickListener mItemListener = new OnItemClickListener() {
25954e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        @Override
26054e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
261ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            final DocumentInfo doc = mAdapter.getItem(position);
262a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey            if (mFilter.apply(doc)) {
263a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey                ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
264a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey            }
26554e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        }
26654e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey    };
26754e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
268be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey    private MultiChoiceModeListener mMultiListener = new MultiChoiceModeListener() {
269be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        @Override
270be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
271be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
272be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            return true;
273be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        }
274be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey
275be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        @Override
276be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2774eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            final DisplayState state = getDisplayState(DirectoryFragment.this);
2784eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
2794eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            final MenuItem open = menu.findItem(R.id.menu_open);
2804eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            final MenuItem share = menu.findItem(R.id.menu_share);
2814eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            final MenuItem delete = menu.findItem(R.id.menu_delete);
2824eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
2834eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            final boolean manageMode = state.action == ACTION_MANAGE;
2844eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            open.setVisible(!manageMode);
2854eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            share.setVisible(manageMode);
2864eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            delete.setVisible(manageMode);
2874eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
288be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            return true;
289be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        }
290be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey
291be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        @Override
292be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2934eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            final SparseBooleanArray checked = mCurrentView.getCheckedItemPositions();
294ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            final ArrayList<DocumentInfo> docs = Lists.newArrayList();
2954eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            final int size = checked.size();
2964eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            for (int i = 0; i < size; i++) {
2974eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                if (checked.valueAt(i)) {
298ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                    final DocumentInfo doc = mAdapter.getItem(checked.keyAt(i));
2994eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                    docs.add(doc);
300be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey                }
3014eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            }
3024eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
3034eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            final int id = item.getItemId();
3044eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            if (id == R.id.menu_open) {
3054eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                DocumentsActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
3064eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                return true;
307be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey
3084eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            } else if (id == R.id.menu_share) {
3094eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                onShareDocuments(docs);
310be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey                return true;
3114eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
3124eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            } else if (id == R.id.menu_delete) {
3134eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                onDeleteDocuments(docs);
3144eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                return true;
3154eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
316be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            } else {
317be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey                return false;
318be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            }
319be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        }
320be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey
321be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        @Override
322be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        public void onDestroyActionMode(ActionMode mode) {
323be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            // ignored
324be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        }
325be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey
326be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        @Override
327be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        public void onItemCheckedStateChanged(
328be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey                ActionMode mode, int position, long id, boolean checked) {
329be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            if (checked) {
330be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey                // Directories cannot be checked
331ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                final DocumentInfo doc = mAdapter.getItem(position);
332b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                if (doc.isDirectory()) {
333be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey                    mCurrentView.setItemChecked(position, false);
334be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey                }
335be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            }
336be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey
337be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey            mode.setTitle(getResources()
338be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey                    .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount()));
339be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey        }
340be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey    };
341be8b12e687bd10a526b1f54c2d8a52abdad15d85Jeff Sharkey
342ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    private void onShareDocuments(List<DocumentInfo> docs) {
3434eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        final ArrayList<Uri> uris = Lists.newArrayList();
344ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        for (DocumentInfo doc : docs) {
3454eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            uris.add(doc.uri);
3464eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        }
3474eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
3484eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        final Intent intent;
3494eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        if (uris.size() > 1) {
3504eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
3514eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
3524eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            intent.addCategory(Intent.CATEGORY_DEFAULT);
3534eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            // TODO: find common mimetype
3544eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            intent.setType("*/*");
3554eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
3564eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        } else {
3574eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            intent = new Intent(Intent.ACTION_SEND);
3584eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
3594eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            intent.addCategory(Intent.CATEGORY_DEFAULT);
3604eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            intent.setData(uris.get(0));
3614eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        }
3624eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
3634eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        startActivity(intent);
3644eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey    }
3654eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
366ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey    private void onDeleteDocuments(List<DocumentInfo> docs) {
3674eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        final Context context = getActivity();
3684eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        final ContentResolver resolver = context.getContentResolver();
3694eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
3704eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        boolean hadTrouble = false;
371ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        for (DocumentInfo doc : docs) {
3724eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            if (!doc.isDeleteSupported()) {
3734eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                Log.w(TAG, "Skipping " + doc);
3744eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                hadTrouble = true;
3754eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                continue;
3764eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            }
3774eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
3784eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            try {
3794eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                if (resolver.delete(doc.uri, null, null) != 1) {
3804eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                    Log.w(TAG, "Failed to delete " + doc);
3814eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                    hadTrouble = true;
3824eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                }
3834eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            } catch (Exception e) {
3844eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                Log.w(TAG, "Failed to delete " + doc + ": " + e);
3854eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                hadTrouble = true;
3864eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            }
3874eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        }
3884eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
3894eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        if (hadTrouble) {
3904eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            Toast.makeText(context, R.string.toast_failed_delete, Toast.LENGTH_SHORT).show();
3914eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        }
3924eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey    }
3934eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
3941482789374fb8da3abea8f6f4f272a67205b95dbJeff Sharkey    private static DisplayState getDisplayState(Fragment fragment) {
3951482789374fb8da3abea8f6f4f272a67205b95dbJeff Sharkey        return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
39654e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey    }
39754e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
398ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey    private class DocumentsAdapter extends BaseAdapter {
399ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        private List<DocumentInfo> mDocuments;
400ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey
401ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        public DocumentsAdapter() {
4029e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        }
4039e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
404ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public void swapDocuments(List<DocumentInfo> documents) {
405ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            mDocuments = documents;
4069fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey
407b448660a227f93d7bd5c5d019a21745e67c2f54dJeff Sharkey            if (mDocuments != null && mDocuments.isEmpty()) {
4089fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey                mEmptyView.setVisibility(View.VISIBLE);
4099fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey            } else {
4109fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey                mEmptyView.setVisibility(View.GONE);
4119fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey            }
4129fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey
413ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            notifyDataSetChanged();
4149e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        }
4159e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
4169e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey        @Override
417ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        public View getView(int position, View convertView, ViewGroup parent) {
418ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            final Context context = parent.getContext();
419b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey            final DisplayState state = getDisplayState(DirectoryFragment.this);
420ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey
4214eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            final RootsCache roots = DocumentsApplication.getRootsCache(context);
4224eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
4234eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                    context, mThumbSize);
4244eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey
425ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            if (convertView == null) {
426ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey                final LayoutInflater inflater = LayoutInflater.from(context);
4274eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                if (state.mode == MODE_LIST) {
428ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey                    convertView = inflater.inflate(R.layout.item_doc_list, parent, false);
4294eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                } else if (state.mode == MODE_GRID) {
430ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey                    convertView = inflater.inflate(R.layout.item_doc_grid, parent, false);
431ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey                } else {
432ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey                    throw new IllegalStateException();
433ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey                }
434ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            }
435ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey
436ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey            final DocumentInfo doc = getItem(position);
437ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey
438b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey            final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
439ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
4409fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey            final View summaryGrid = convertView.findViewById(R.id.summary_grid);
441b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey            final ImageView icon1 = (ImageView) convertView.findViewById(android.R.id.icon1);
442ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
443b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey            final TextView date = (TextView) convertView.findViewById(R.id.date);
444b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey            final TextView size = (TextView) convertView.findViewById(R.id.size);
445ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey
4469d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) icon.getTag();
4479d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            if (oldTask != null) {
4489d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                oldTask.cancel(false);
4499d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            }
4509d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
451ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            if (doc.isThumbnailSupported()) {
4524eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                final Bitmap cachedResult = thumbs.get(doc.uri);
4539d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                if (cachedResult != null) {
4549d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                    icon.setImageBitmap(cachedResult);
4559d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                } else {
4569d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                    final ThumbnailAsyncTask task = new ThumbnailAsyncTask(icon, mThumbSize);
4579d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                    icon.setImageBitmap(null);
4589d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                    icon.setTag(task);
4599d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                    task.execute(doc.uri);
4609d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                }
4619e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey            } else {
462aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                icon.setImageDrawable(RootsCache.resolveDocumentIcon(context, doc.mimeType));
4639e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey            }
4649e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
465ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            title.setText(doc.displayName);
466b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey
467b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey            if (mType == TYPE_NORMAL || mType == TYPE_SEARCH) {
468b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                icon1.setVisibility(View.GONE);
469b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                if (doc.summary != null) {
470b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                    summary.setText(doc.summary);
471b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                    summary.setVisibility(View.VISIBLE);
472b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                } else {
473b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                    summary.setVisibility(View.INVISIBLE);
474b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                }
475b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey            } else if (mType == TYPE_RECENT_OPEN) {
476aeb16e2435f9975b9fa1fc4b747796647a21292eJeff Sharkey                // TODO: resolve storage root
477b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey            }
478b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey
4799fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey            if (summaryGrid != null) {
4809fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey                summaryGrid.setVisibility(
4819fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey                        (summary.getVisibility() == View.VISIBLE) ? View.VISIBLE : View.GONE);
4829fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey            }
4839fb567b59112f99e64e0bff6f343188331bad28dJeff Sharkey
4849d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            if (doc.lastModified == -1) {
4859d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                date.setText(null);
4869d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            } else {
4879d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                date.setText(formatTime(context, doc.lastModified));
4889d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            }
489b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey
490b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey            if (state.showSize) {
491b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                size.setVisibility(View.VISIBLE);
492a5599ef636e37cb0b6474349936999be1afe6987Jeff Sharkey                if (doc.isDirectory() || doc.size == -1) {
493b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                    size.setText(null);
494b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                } else {
495b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                    size.setText(Formatter.formatFileSize(context, doc.size));
496b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                }
497b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey            } else {
498b156f4bf8cdfe475a7116b627d84a281e1a281b7Jeff Sharkey                size.setVisibility(View.GONE);
49954e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey            }
500ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey
501ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            return convertView;
50254e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey        }
50354e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
504ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        @Override
505ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        public int getCount() {
506ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            return mDocuments != null ? mDocuments.size() : 0;
507ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        }
5089e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey
509ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        @Override
510ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey        public DocumentInfo getItem(int position) {
511ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            return mDocuments.get(position);
512ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        }
51354e55b740fef1be654c3959aee41ef5ddfa61293Jeff Sharkey
514ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        @Override
515ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        public long getItemId(int position) {
516ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey            return getItem(position).uri.hashCode();
517ef7184a1aa0be5d496a5cb495a0f9e11f342af44Jeff Sharkey        }
5189e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey    }
5199d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
5209d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey    private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> {
5219d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        private final ImageView mTarget;
5224eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        private final Point mThumbSize;
5239d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
5244eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey        public ThumbnailAsyncTask(ImageView target, Point thumbSize) {
5259d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            mTarget = target;
5264eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey            mThumbSize = thumbSize;
5279d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        }
5289d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
5299d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        @Override
5309d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        protected void onPreExecute() {
5319d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            mTarget.setTag(this);
5329d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        }
5339d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
5349d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        @Override
5359d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        protected Bitmap doInBackground(Uri... params) {
5369d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            final Context context = mTarget.getContext();
5379d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            final Uri uri = params[0];
5389d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
5399d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            Bitmap result = null;
5409d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            try {
541ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                result = DocumentsContract.getDocumentThumbnail(
542ae9b51bfa313c51a31af30875a71255d7b6d2e61Jeff Sharkey                        context.getContentResolver(), uri, mThumbSize, null);
5439d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                if (result != null) {
5444eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                    final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
5454eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                            context, mThumbSize);
5464eb407a832b7d6a2d62a535e5cab70b00a0bc8edJeff Sharkey                    thumbs.put(uri, result);
5479d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                }
5489d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            } catch (Exception e) {
5499d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                Log.w(TAG, "Failed to load thumbnail: " + e);
5509d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            }
5519d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            return result;
5529d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        }
5539d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
5549d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        @Override
5559d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        protected void onPostExecute(Bitmap result) {
5569d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            if (mTarget.getTag() == this) {
5579d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                mTarget.setImageBitmap(result);
5589d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                mTarget.setTag(null);
5599d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            }
5609d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        }
5619d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey    }
5629d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
5639d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey    private static String formatTime(Context context, long when) {
5649d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        // TODO: DateUtils should make this easier
5659d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        Time then = new Time();
5669d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        then.set(when);
5679d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        Time now = new Time();
5689d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        now.setToNow();
5699d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
5709d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
5719d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey                | DateUtils.FORMAT_ABBREV_ALL;
5729d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
5739d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        if (then.year != now.year) {
5749d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
5759d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        } else if (then.yearDay != now.yearDay) {
5769d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            flags |= DateUtils.FORMAT_SHOW_DATE;
5779d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        } else {
5789d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey            flags |= DateUtils.FORMAT_SHOW_TIME;
5799d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        }
5809d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey
5819d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey        return DateUtils.formatDateTime(context, when, flags);
5829d0843df7e3984293dc4ab6ee2f9502e898b63aaJeff Sharkey    }
5839e0036ed7d3260d79cc5f9ffd8e3bbe760699924Jeff Sharkey}
584