blob: 4d410d53ee9636cead8c6a128462a4e4bf846aae [file] [log] [blame]
Jeff Sharkeye22d02e2013-04-26 16:54:55 -07001/*
2 * Copyright (C) 2013 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
17package com.android.documentsui;
18
Jeff Sharkey8a8fb672013-05-07 12:41:33 -070019import static com.android.documentsui.DocumentsActivity.TAG;
Jeff Sharkey6defd072013-09-23 14:23:41 -070020import static com.android.documentsui.DocumentsActivity.State.ACTION_CREATE;
Jeff Sharkeyb3620442013-09-01 18:41:04 -070021import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
22import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
23import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070024import static com.android.documentsui.DocumentsActivity.State.MODE_UNKNOWN;
25import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070026import static com.android.documentsui.model.DocumentInfo.getCursorInt;
27import static com.android.documentsui.model.DocumentInfo.getCursorLong;
28import static com.android.documentsui.model.DocumentInfo.getCursorString;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -070029
Jeff Sharkeyf63b7772013-10-01 17:57:41 -070030import android.app.ActivityManager;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070031import android.app.Fragment;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070032import android.app.FragmentManager;
33import android.app.FragmentTransaction;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070034import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey3fd11772013-09-30 14:26:27 -070035import android.content.ContentProviderClient;
Jeff Sharkey873daa32013-08-18 17:38:20 -070036import android.content.ContentResolver;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -070037import android.content.ContentValues;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070038import android.content.Context;
Jeff Sharkey873daa32013-08-18 17:38:20 -070039import android.content.Intent;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070040import android.content.Loader;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070041import android.database.Cursor;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -070042import android.graphics.Bitmap;
43import android.graphics.Point;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070044import android.graphics.drawable.Drawable;
Jeff Sharkeydb884f52013-09-21 15:11:19 -070045import android.graphics.drawable.InsetDrawable;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070046import android.net.Uri;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -070047import android.os.AsyncTask;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070048import android.os.Bundle;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070049import android.os.CancellationSignal;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -070050import android.os.Parcelable;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070051import android.provider.DocumentsContract;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -070052import android.provider.DocumentsContract.Document;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070053import android.text.format.DateUtils;
Jeff Sharkey2e694f82013-08-06 16:26:14 -070054import android.text.format.Formatter;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -070055import android.text.format.Time;
56import android.util.Log;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -070057import android.util.SparseArray;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070058import android.util.SparseBooleanArray;
59import android.view.ActionMode;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070060import android.view.LayoutInflater;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070061import android.view.Menu;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070062import android.view.MenuItem;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070063import android.view.View;
64import android.view.ViewGroup;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070065import android.widget.AbsListView;
66import android.widget.AbsListView.MultiChoiceModeListener;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070067import android.widget.AbsListView.RecyclerListener;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070068import android.widget.AdapterView;
69import android.widget.AdapterView.OnItemClickListener;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070070import android.widget.BaseAdapter;
Jeff Sharkeydb884f52013-09-21 15:11:19 -070071import android.widget.FrameLayout;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070072import android.widget.GridView;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070073import android.widget.ImageView;
74import android.widget.ListView;
75import android.widget.TextView;
Jeff Sharkey873daa32013-08-18 17:38:20 -070076import android.widget.Toast;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070077
Jeff Sharkeyb3620442013-09-01 18:41:04 -070078import com.android.documentsui.DocumentsActivity.State;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -070079import com.android.documentsui.RecentsProvider.StateColumns;
Jeff Sharkey724deeb2013-08-31 15:02:20 -070080import com.android.documentsui.model.DocumentInfo;
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -070081import com.android.documentsui.model.RootInfo;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070082import com.google.android.collect.Lists;
83
84import java.util.ArrayList;
Jeff Sharkeya5defe32013-08-05 17:56:48 -070085import java.util.List;
Jeff Sharkey2e694f82013-08-06 16:26:14 -070086import java.util.concurrent.atomic.AtomicInteger;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070087
88/**
89 * Display the documents inside a single directory.
90 */
91public class DirectoryFragment extends Fragment {
92
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -070093 private View mEmptyView;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070094 private ListView mListView;
95 private GridView mGridView;
96
Jeff Sharkeyc317af82013-07-01 16:56:54 -070097 private AbsListView mCurrentView;
98
Jeff Sharkeya5defe32013-08-05 17:56:48 -070099 public static final int TYPE_NORMAL = 1;
100 public static final int TYPE_SEARCH = 2;
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700101 public static final int TYPE_RECENT_OPEN = 3;
Jeff Sharkey5b535922013-08-02 15:55:26 -0700102
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700103 public static final int ANIM_NONE = 1;
104 public static final int ANIM_SIDE = 2;
105 public static final int ANIM_DOWN = 3;
106 public static final int ANIM_UP = 4;
107
Jeff Sharkey5b535922013-08-02 15:55:26 -0700108 private int mType = TYPE_NORMAL;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700109 private String mStateKey;
Jeff Sharkey5b535922013-08-02 15:55:26 -0700110
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700111 private int mLastMode = MODE_UNKNOWN;
112 private int mLastSortOrder = SORT_ORDER_UNKNOWN;
Jeff Sharkey5e1884d2013-09-10 17:56:39 -0700113 private boolean mLastShowSize = false;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700114
Jeff Sharkey9656a532013-09-13 13:42:19 -0700115 private boolean mHideGridTitles = false;
116
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700117 private boolean mSvelteRecents;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700118 private Point mThumbSize;
119
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700120 private DocumentsAdapter mAdapter;
Jeff Sharkey46899c82013-08-18 22:26:48 -0700121 private LoaderCallbacks<DirectoryResult> mCallbacks;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700122
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700123 private static final String EXTRA_TYPE = "type";
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700124 private static final String EXTRA_ROOT = "root";
125 private static final String EXTRA_DOC = "doc";
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700126 private static final String EXTRA_QUERY = "query";
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700127 private static final String EXTRA_IGNORE_STATE = "ignoreState";
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700128
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700129 private static AtomicInteger sLoaderId = new AtomicInteger(4000);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700130
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700131 private final int mLoaderId = sLoaderId.incrementAndGet();
132
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700133 public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
134 show(fm, TYPE_NORMAL, root, doc, null, anim);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700135 }
136
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700137 public static void showSearch(FragmentManager fm, RootInfo root, String query, int anim) {
138 show(fm, TYPE_SEARCH, root, null, query, anim);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700139 }
140
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700141 public static void showRecentsOpen(FragmentManager fm, int anim) {
142 show(fm, TYPE_RECENT_OPEN, null, null, null, anim);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700143 }
144
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700145 private static void show(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
146 String query, int anim) {
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700147 final Bundle args = new Bundle();
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700148 args.putInt(EXTRA_TYPE, type);
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700149 args.putParcelable(EXTRA_ROOT, root);
150 args.putParcelable(EXTRA_DOC, doc);
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700151 args.putString(EXTRA_QUERY, query);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700152
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700153 final FragmentTransaction ft = fm.beginTransaction();
154 switch (anim) {
155 case ANIM_SIDE:
156 args.putBoolean(EXTRA_IGNORE_STATE, true);
157 break;
158 case ANIM_DOWN:
159 args.putBoolean(EXTRA_IGNORE_STATE, true);
160 ft.setCustomAnimations(R.animator.dir_down, R.animator.dir_frozen);
161 break;
162 case ANIM_UP:
163 ft.setCustomAnimations(R.animator.dir_frozen, R.animator.dir_up);
164 break;
165 }
166
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700167 final DirectoryFragment fragment = new DirectoryFragment();
168 fragment.setArguments(args);
169
Jeff Sharkey76112212013-08-06 11:26:10 -0700170 ft.replace(R.id.container_directory, fragment);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700171 ft.commitAllowingStateLoss();
172 }
173
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700174 private static String buildStateKey(RootInfo root, DocumentInfo doc) {
175 final StringBuilder builder = new StringBuilder();
176 builder.append(root != null ? root.authority : "null").append(';');
177 builder.append(root != null ? root.rootId : "null").append(';');
178 builder.append(doc != null ? doc.documentId : "null");
179 return builder.toString();
180 }
181
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700182 public static DirectoryFragment get(FragmentManager fm) {
183 // TODO: deal with multiple directories shown at once
Jeff Sharkey76112212013-08-06 11:26:10 -0700184 return (DirectoryFragment) fm.findFragmentById(R.id.container_directory);
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700185 }
186
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700187 @Override
188 public View onCreateView(
189 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
190 final Context context = inflater.getContext();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700191 final View view = inflater.inflate(R.layout.fragment_directory, container, false);
192
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700193 mEmptyView = view.findViewById(android.R.id.empty);
194
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700195 mListView = (ListView) view.findViewById(R.id.list);
196 mListView.setOnItemClickListener(mItemListener);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700197 mListView.setMultiChoiceModeListener(mMultiListener);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700198 mListView.setRecyclerListener(mRecycleListener);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700199
200 mGridView = (GridView) view.findViewById(R.id.grid);
201 mGridView.setOnItemClickListener(mItemListener);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700202 mGridView.setMultiChoiceModeListener(mMultiListener);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700203 mGridView.setRecyclerListener(mRecycleListener);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700204
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700205 return view;
206 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700207
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700208 @Override
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700209 public void onDestroyView() {
210 super.onDestroyView();
211
212 // Cancel any outstanding thumbnail requests
213 final ViewGroup target = (mListView.getAdapter() != null) ? mListView : mGridView;
214 final int count = target.getChildCount();
215 for (int i = 0; i < count; i++) {
216 final View view = target.getChildAt(i);
217 mRecycleListener.onMovedToScrapHeap(view);
218 }
Jeff Sharkeyfaaeb392013-10-04 14:44:56 -0700219
220 // Tear down any selection in progress
221 mListView.setChoiceMode(AbsListView.CHOICE_MODE_NONE);
222 mGridView.setChoiceMode(AbsListView.CHOICE_MODE_NONE);
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700223 }
224
225 @Override
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700226 public void onActivityCreated(Bundle savedInstanceState) {
227 super.onActivityCreated(savedInstanceState);
228
229 final Context context = getActivity();
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700230 final State state = getDisplayState(DirectoryFragment.this);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700231
Jeff Sharkey9656a532013-09-13 13:42:19 -0700232 final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
233 final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
234
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700235 mAdapter = new DocumentsAdapter();
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700236 mType = getArguments().getInt(EXTRA_TYPE);
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700237 mStateKey = buildStateKey(root, doc);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700238
Jeff Sharkey9656a532013-09-13 13:42:19 -0700239 if (mType == TYPE_RECENT_OPEN) {
240 // Hide titles when showing recents for picking images/videos
241 mHideGridTitles = MimePredicate.mimeMatches(
242 MimePredicate.VISUAL_MIMES, state.acceptMimes);
243 } else {
244 mHideGridTitles = (doc != null) && doc.isGridTitlesHidden();
245 }
246
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700247 final ActivityManager am = (ActivityManager) context.getSystemService(
248 Context.ACTIVITY_SERVICE);
249 mSvelteRecents = am.isLowRamDevice() && (mType == TYPE_RECENT_OPEN);
250
Jeff Sharkey46899c82013-08-18 22:26:48 -0700251 mCallbacks = new LoaderCallbacks<DirectoryResult>() {
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700252 @Override
Jeff Sharkey46899c82013-08-18 22:26:48 -0700253 public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700254 final String query = getArguments().getString(EXTRA_QUERY);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700255
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700256 Uri contentsUri;
257 switch (mType) {
258 case TYPE_NORMAL:
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700259 contentsUri = DocumentsContract.buildChildDocumentsUri(
260 doc.authority, doc.documentId);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700261 if (state.action == ACTION_MANAGE) {
262 contentsUri = DocumentsContract.setManageMode(contentsUri);
263 }
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700264 return new DirectoryLoader(
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700265 context, mType, root, doc, contentsUri, state.userSortOrder);
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700266 case TYPE_SEARCH:
267 contentsUri = DocumentsContract.buildSearchDocumentsUri(
Jeff Sharkey0e8c8712013-09-12 21:59:06 -0700268 root.authority, root.rootId, query);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700269 if (state.action == ACTION_MANAGE) {
270 contentsUri = DocumentsContract.setManageMode(contentsUri);
271 }
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700272 return new DirectoryLoader(
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700273 context, mType, root, doc, contentsUri, state.userSortOrder);
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700274 case TYPE_RECENT_OPEN:
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700275 final RootsCache roots = DocumentsApplication.getRootsCache(context);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700276 return new RecentLoader(context, roots, state);
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700277 default:
278 throw new IllegalStateException("Unknown type " + mType);
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700279 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700280 }
281
282 @Override
Jeff Sharkey46899c82013-08-18 22:26:48 -0700283 public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700284 if (!isAdded()) return;
285
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700286 mAdapter.swapResult(result);
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700287
288 // Push latest state up to UI
289 // TODO: if mode change was racing with us, don't overwrite it
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700290 if (result.mode != MODE_UNKNOWN) {
291 state.derivedMode = result.mode;
292 }
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700293 state.derivedSortOrder = result.sortOrder;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700294 ((DocumentsActivity) context).onStateChanged();
295
296 updateDisplayState();
297
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700298 // Restore any previous instance state
299 final SparseArray<Parcelable> container = state.dirState.remove(mStateKey);
300 if (container != null && !getArguments().getBoolean(EXTRA_IGNORE_STATE, false)) {
301 getView().restoreHierarchyState(container);
302 } else if (mLastSortOrder != state.derivedSortOrder) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700303 mListView.smoothScrollToPosition(0);
304 mGridView.smoothScrollToPosition(0);
305 }
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700306
307 mLastSortOrder = state.derivedSortOrder;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700308 }
309
310 @Override
Jeff Sharkey46899c82013-08-18 22:26:48 -0700311 public void onLoaderReset(Loader<DirectoryResult> loader) {
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700312 mAdapter.swapResult(null);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700313 }
314 };
315
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700316 // Kick off loader at least once
317 getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
318
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700319 updateDisplayState();
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700320 }
321
Jeff Sharkey42d26792013-09-06 13:22:09 -0700322 @Override
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700323 public void onStop() {
324 super.onStop();
325
326 // Remember last scroll location
327 final SparseArray<Parcelable> container = new SparseArray<Parcelable>();
328 getView().saveHierarchyState(container);
329 final State state = getDisplayState(this);
330 state.dirState.put(mStateKey, container);
331 }
332
333 @Override
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700334 public void onResume() {
335 super.onResume();
Jeff Sharkey42d26792013-09-06 13:22:09 -0700336 updateDisplayState();
337 }
338
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700339 public void onUserSortOrderChanged() {
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700340 // Sort order change always triggers reload; we'll trigger state change
341 // on the flip side.
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700342 getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
343 }
344
345 public void onUserModeChanged() {
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700346 final ContentResolver resolver = getActivity().getContentResolver();
347 final State state = getDisplayState(this);
348
349 final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
350 final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
351
Jeff Sharkey0e8c8712013-09-12 21:59:06 -0700352 if (root != null && doc != null) {
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700353 final Uri stateUri = RecentsProvider.buildState(
354 root.authority, root.rootId, doc.documentId);
355 final ContentValues values = new ContentValues();
356 values.put(StateColumns.MODE, state.userMode);
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700357
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700358 new AsyncTask<Void, Void, Void>() {
359 @Override
360 protected Void doInBackground(Void... params) {
361 resolver.insert(stateUri, values);
362 return null;
363 }
364 }.execute();
365 }
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700366
367 // Mode change is just visual change; no need to kick loader, and
368 // deliver change event immediately.
369 state.derivedMode = state.userMode;
370 ((DocumentsActivity) getActivity()).onStateChanged();
371
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700372 updateDisplayState();
373 }
374
375 private void updateDisplayState() {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700376 final State state = getDisplayState(this);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700377
Jeff Sharkey5e1884d2013-09-10 17:56:39 -0700378 if (mLastMode == state.derivedMode && mLastShowSize == state.showSize) return;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700379 mLastMode = state.derivedMode;
Jeff Sharkey5e1884d2013-09-10 17:56:39 -0700380 mLastShowSize = state.showSize;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700381
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700382 mListView.setVisibility(state.derivedMode == MODE_LIST ? View.VISIBLE : View.GONE);
383 mGridView.setVisibility(state.derivedMode == MODE_GRID ? View.VISIBLE : View.GONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700384
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700385 final int choiceMode;
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700386 if (state.allowMultiple) {
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700387 choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
388 } else {
389 choiceMode = ListView.CHOICE_MODE_NONE;
390 }
391
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700392 final int thumbSize;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700393 if (state.derivedMode == MODE_GRID) {
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700394 thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700395 mListView.setAdapter(null);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700396 mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700397 mGridView.setAdapter(mAdapter);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700398 mGridView.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.grid_width));
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700399 mGridView.setNumColumns(GridView.AUTO_FIT);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700400 mGridView.setChoiceMode(choiceMode);
401 mCurrentView = mGridView;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700402 } else if (state.derivedMode == MODE_LIST) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700403 thumbSize = getResources().getDimensionPixelSize(R.dimen.icon_size);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700404 mGridView.setAdapter(null);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700405 mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700406 mListView.setAdapter(mAdapter);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700407 mListView.setChoiceMode(choiceMode);
408 mCurrentView = mListView;
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700409 } else {
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700410 throw new IllegalStateException("Unknown state " + state.derivedMode);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700411 }
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700412
413 mThumbSize = new Point(thumbSize, thumbSize);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700414 }
415
416 private OnItemClickListener mItemListener = new OnItemClickListener() {
417 @Override
418 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700419 final Cursor cursor = mAdapter.getItem(position);
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700420 if (cursor != null) {
Jeff Sharkey7cf49032013-09-26 10:54:16 -0700421 final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
422 final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
423 if (isDocumentEnabled(docMimeType, docFlags)) {
424 final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700425 ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
426 }
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700427 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700428 }
429 };
430
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700431 private MultiChoiceModeListener mMultiListener = new MultiChoiceModeListener() {
432 @Override
433 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
434 mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
435 return true;
436 }
437
438 @Override
439 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700440 final State state = getDisplayState(DirectoryFragment.this);
Jeff Sharkey873daa32013-08-18 17:38:20 -0700441
442 final MenuItem open = menu.findItem(R.id.menu_open);
443 final MenuItem share = menu.findItem(R.id.menu_share);
444 final MenuItem delete = menu.findItem(R.id.menu_delete);
445
446 final boolean manageMode = state.action == ACTION_MANAGE;
447 open.setVisible(!manageMode);
448 share.setVisible(manageMode);
449 delete.setVisible(manageMode);
450
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700451 return true;
452 }
453
454 @Override
455 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700456 final SparseBooleanArray checked = mCurrentView.getCheckedItemPositions();
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700457 final ArrayList<DocumentInfo> docs = Lists.newArrayList();
Jeff Sharkey873daa32013-08-18 17:38:20 -0700458 final int size = checked.size();
459 for (int i = 0; i < size; i++) {
460 if (checked.valueAt(i)) {
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700461 final Cursor cursor = mAdapter.getItem(checked.keyAt(i));
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700462 final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
Jeff Sharkey873daa32013-08-18 17:38:20 -0700463 docs.add(doc);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700464 }
Jeff Sharkey873daa32013-08-18 17:38:20 -0700465 }
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700466
Jeff Sharkey873daa32013-08-18 17:38:20 -0700467 final int id = item.getItemId();
468 if (id == R.id.menu_open) {
469 DocumentsActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700470 mode.finish();
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700471 return true;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700472
473 } else if (id == R.id.menu_share) {
474 onShareDocuments(docs);
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700475 mode.finish();
Jeff Sharkey873daa32013-08-18 17:38:20 -0700476 return true;
477
478 } else if (id == R.id.menu_delete) {
479 onDeleteDocuments(docs);
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700480 mode.finish();
Jeff Sharkey873daa32013-08-18 17:38:20 -0700481 return true;
482
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700483 } else {
484 return false;
485 }
486 }
487
488 @Override
489 public void onDestroyActionMode(ActionMode mode) {
490 // ignored
491 }
492
493 @Override
494 public void onItemCheckedStateChanged(
495 ActionMode mode, int position, long id, boolean checked) {
496 if (checked) {
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700497 // Directories and footer items cannot be checked
498 boolean valid = false;
499
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700500 final Cursor cursor = mAdapter.getItem(position);
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700501 if (cursor != null) {
502 final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
Jeff Sharkey7cf49032013-09-26 10:54:16 -0700503 final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
504 if (!Document.MIME_TYPE_DIR.equals(docMimeType)) {
505 valid = isDocumentEnabled(docMimeType, docFlags);
506 }
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700507 }
508
509 if (!valid) {
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700510 mCurrentView.setItemChecked(position, false);
511 }
512 }
513
514 mode.setTitle(getResources()
515 .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount()));
516 }
517 };
518
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700519 private RecyclerListener mRecycleListener = new RecyclerListener() {
520 @Override
521 public void onMovedToScrapHeap(View view) {
522 final ImageView iconThumb = (ImageView) view.findViewById(R.id.icon_thumb);
523 if (iconThumb != null) {
524 final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) iconThumb.getTag();
525 if (oldTask != null) {
526 oldTask.reallyCancel();
527 iconThumb.setTag(null);
528 }
529 }
530 }
531 };
532
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700533 private void onShareDocuments(List<DocumentInfo> docs) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700534 Intent intent;
535 if (docs.size() == 1) {
536 final DocumentInfo doc = docs.get(0);
Jeff Sharkey873daa32013-08-18 17:38:20 -0700537
Jeff Sharkey873daa32013-08-18 17:38:20 -0700538 intent = new Intent(Intent.ACTION_SEND);
539 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
540 intent.addCategory(Intent.CATEGORY_DEFAULT);
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700541 intent.setType(doc.mimeType);
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700542 intent.putExtra(Intent.EXTRA_STREAM, doc.derivedUri);
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700543
544 } else if (docs.size() > 1) {
545 intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
546 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
547 intent.addCategory(Intent.CATEGORY_DEFAULT);
548
549 final ArrayList<String> mimeTypes = Lists.newArrayList();
550 final ArrayList<Uri> uris = Lists.newArrayList();
551 for (DocumentInfo doc : docs) {
552 mimeTypes.add(doc.mimeType);
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700553 uris.add(doc.derivedUri);
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700554 }
555
556 intent.setType(findCommonMimeType(mimeTypes));
557 intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
558
559 } else {
560 return;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700561 }
562
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700563 intent = Intent.createChooser(intent, getActivity().getText(R.string.share_via));
Jeff Sharkey873daa32013-08-18 17:38:20 -0700564 startActivity(intent);
565 }
566
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700567 private void onDeleteDocuments(List<DocumentInfo> docs) {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700568 final Context context = getActivity();
569 final ContentResolver resolver = context.getContentResolver();
570
571 boolean hadTrouble = false;
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700572 for (DocumentInfo doc : docs) {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700573 if (!doc.isDeleteSupported()) {
574 Log.w(TAG, "Skipping " + doc);
575 hadTrouble = true;
576 continue;
577 }
578
Jeff Sharkey3fd11772013-09-30 14:26:27 -0700579 ContentProviderClient client = null;
580 try {
581 client = DocumentsApplication.acquireUnstableProviderOrThrow(
582 resolver, doc.derivedUri.getAuthority());
583 DocumentsContract.deleteDocument(client, doc.derivedUri);
584 } catch (Exception e) {
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700585 Log.w(TAG, "Failed to delete " + doc);
Jeff Sharkey873daa32013-08-18 17:38:20 -0700586 hadTrouble = true;
Jeff Sharkey3fd11772013-09-30 14:26:27 -0700587 } finally {
588 ContentProviderClient.releaseQuietly(client);
Jeff Sharkey873daa32013-08-18 17:38:20 -0700589 }
590 }
591
592 if (hadTrouble) {
593 Toast.makeText(context, R.string.toast_failed_delete, Toast.LENGTH_SHORT).show();
594 }
595 }
596
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700597 private static State getDisplayState(Fragment fragment) {
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700598 return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700599 }
600
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700601 private static abstract class Footer {
602 private final int mItemViewType;
603
604 public Footer(int itemViewType) {
605 mItemViewType = itemViewType;
606 }
607
608 public abstract View getView(View convertView, ViewGroup parent);
609
610 public int getItemViewType() {
611 return mItemViewType;
612 }
Jeff Sharkey20b32272013-09-03 15:25:52 -0700613 }
614
Jeff Sharkey5e1884d2013-09-10 17:56:39 -0700615 private class LoadingFooter extends Footer {
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700616 public LoadingFooter() {
617 super(1);
618 }
619
Jeff Sharkey20b32272013-09-03 15:25:52 -0700620 @Override
621 public View getView(View convertView, ViewGroup parent) {
622 final Context context = parent.getContext();
Jeff Sharkey5e1884d2013-09-10 17:56:39 -0700623 final State state = getDisplayState(DirectoryFragment.this);
624
Jeff Sharkey20b32272013-09-03 15:25:52 -0700625 if (convertView == null) {
626 final LayoutInflater inflater = LayoutInflater.from(context);
Jeff Sharkey5e1884d2013-09-10 17:56:39 -0700627 if (state.derivedMode == MODE_LIST) {
628 convertView = inflater.inflate(R.layout.item_loading_list, parent, false);
629 } else if (state.derivedMode == MODE_GRID) {
630 convertView = inflater.inflate(R.layout.item_loading_grid, parent, false);
631 } else {
632 throw new IllegalStateException();
633 }
Jeff Sharkey20b32272013-09-03 15:25:52 -0700634 }
Jeff Sharkey5e1884d2013-09-10 17:56:39 -0700635
Jeff Sharkey20b32272013-09-03 15:25:52 -0700636 return convertView;
637 }
638 }
639
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700640 private class MessageFooter extends Footer {
Jeff Sharkey20b32272013-09-03 15:25:52 -0700641 private final int mIcon;
642 private final String mMessage;
643
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700644 public MessageFooter(int itemViewType, int icon, String message) {
645 super(itemViewType);
Jeff Sharkey20b32272013-09-03 15:25:52 -0700646 mIcon = icon;
647 mMessage = message;
648 }
649
650 @Override
651 public View getView(View convertView, ViewGroup parent) {
652 final Context context = parent.getContext();
653 final State state = getDisplayState(DirectoryFragment.this);
654
655 if (convertView == null) {
656 final LayoutInflater inflater = LayoutInflater.from(context);
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700657 if (state.derivedMode == MODE_LIST) {
Jeff Sharkey20b32272013-09-03 15:25:52 -0700658 convertView = inflater.inflate(R.layout.item_message_list, parent, false);
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700659 } else if (state.derivedMode == MODE_GRID) {
Jeff Sharkey20b32272013-09-03 15:25:52 -0700660 convertView = inflater.inflate(R.layout.item_message_grid, parent, false);
661 } else {
662 throw new IllegalStateException();
663 }
664 }
665
666 final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
667 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
668 icon.setImageResource(mIcon);
669 title.setText(mMessage);
670 return convertView;
671 }
672 }
673
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700674 private class DocumentsAdapter extends BaseAdapter {
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700675 private Cursor mCursor;
Jeff Sharkey20b32272013-09-03 15:25:52 -0700676 private int mCursorCount;
677
678 private List<Footer> mFooters = Lists.newArrayList();
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700679
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700680 public void swapResult(DirectoryResult result) {
681 mCursor = result != null ? result.cursor : null;
682 mCursorCount = mCursor != null ? mCursor.getCount() : 0;
Jeff Sharkey20b32272013-09-03 15:25:52 -0700683
684 mFooters.clear();
685
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700686 final Bundle extras = mCursor != null ? mCursor.getExtras() : null;
Jeff Sharkey20b32272013-09-03 15:25:52 -0700687 if (extras != null) {
688 final String info = extras.getString(DocumentsContract.EXTRA_INFO);
689 if (info != null) {
Jeff Sharkeydb884f52013-09-21 15:11:19 -0700690 mFooters.add(new MessageFooter(2, R.drawable.ic_dialog_info, info));
Jeff Sharkey20b32272013-09-03 15:25:52 -0700691 }
692 final String error = extras.getString(DocumentsContract.EXTRA_ERROR);
693 if (error != null) {
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700694 mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, error));
Jeff Sharkey20b32272013-09-03 15:25:52 -0700695 }
696 if (extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)) {
697 mFooters.add(new LoadingFooter());
698 }
699 }
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700700
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700701 if (result != null && result.exception != null) {
Jeff Sharkey3fd11772013-09-30 14:26:27 -0700702 mFooters.add(new MessageFooter(
703 3, R.drawable.ic_dialog_alert, getString(R.string.query_error)));
704 }
705
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700706 if (isEmpty()) {
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700707 mEmptyView.setVisibility(View.VISIBLE);
708 } else {
709 mEmptyView.setVisibility(View.GONE);
710 }
711
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700712 notifyDataSetChanged();
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700713 }
714
715 @Override
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700716 public View getView(int position, View convertView, ViewGroup parent) {
Jeff Sharkey20b32272013-09-03 15:25:52 -0700717 if (position < mCursorCount) {
718 return getDocumentView(position, convertView, parent);
719 } else {
720 position -= mCursorCount;
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700721 convertView = mFooters.get(position).getView(convertView, parent);
722 // Only the view itself is disabled; contents inside shouldn't
723 // be dimmed.
724 convertView.setEnabled(false);
725 return convertView;
Jeff Sharkey20b32272013-09-03 15:25:52 -0700726 }
727 }
728
729 private View getDocumentView(int position, View convertView, ViewGroup parent) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700730 final Context context = parent.getContext();
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700731 final State state = getDisplayState(DirectoryFragment.this);
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700732
Jeff Sharkey9656a532013-09-13 13:42:19 -0700733 final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
734
Jeff Sharkey873daa32013-08-18 17:38:20 -0700735 final RootsCache roots = DocumentsApplication.getRootsCache(context);
736 final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
737 context, mThumbSize);
738
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700739 if (convertView == null) {
740 final LayoutInflater inflater = LayoutInflater.from(context);
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700741 if (state.derivedMode == MODE_LIST) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700742 convertView = inflater.inflate(R.layout.item_doc_list, parent, false);
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700743 } else if (state.derivedMode == MODE_GRID) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700744 convertView = inflater.inflate(R.layout.item_doc_grid, parent, false);
Jeff Sharkeydb884f52013-09-21 15:11:19 -0700745
746 // Apply padding to grid items
747 final FrameLayout grid = (FrameLayout) convertView;
748 final int gridPadding = getResources()
749 .getDimensionPixelSize(R.dimen.grid_padding);
Jeff Sharkey38ec2522013-09-24 12:07:12 -0700750
751 // Tricksy hobbitses! We need to fully clear the drawable so
752 // the view doesn't clobber the new InsetDrawable callback
753 // when setting back later.
754 final Drawable fg = grid.getForeground();
755 final Drawable bg = grid.getBackground();
756 grid.setForeground(null);
757 grid.setBackground(null);
758 grid.setForeground(new InsetDrawable(fg, gridPadding));
759 grid.setBackground(new InsetDrawable(bg, gridPadding));
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700760 } else {
761 throw new IllegalStateException();
762 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700763 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700764
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700765 final Cursor cursor = getItem(position);
766
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700767 final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
768 final String docRootId = getCursorString(cursor, RootCursorWrapper.COLUMN_ROOT_ID);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700769 final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
770 final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
771 final String docDisplayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
772 final long docLastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
773 final int docIcon = getCursorInt(cursor, Document.COLUMN_ICON);
774 final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
775 final String docSummary = getCursorString(cursor, Document.COLUMN_SUMMARY);
776 final long docSize = getCursorLong(cursor, Document.COLUMN_SIZE);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700777
Jeff Sharkey9656a532013-09-13 13:42:19 -0700778 final View line1 = convertView.findViewById(R.id.line1);
779 final View line2 = convertView.findViewById(R.id.line2);
780
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700781 final ImageView iconMime = (ImageView) convertView.findViewById(R.id.icon_mime);
782 final ImageView iconThumb = (ImageView) convertView.findViewById(R.id.icon_thumb);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700783 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
784 final ImageView icon1 = (ImageView) convertView.findViewById(android.R.id.icon1);
Jeff Sharkey9656a532013-09-13 13:42:19 -0700785 final ImageView icon2 = (ImageView) convertView.findViewById(android.R.id.icon2);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700786 final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
787 final TextView date = (TextView) convertView.findViewById(R.id.date);
788 final TextView size = (TextView) convertView.findViewById(R.id.size);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700789
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700790 final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) iconThumb.getTag();
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700791 if (oldTask != null) {
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700792 oldTask.reallyCancel();
793 iconThumb.setTag(null);
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700794 }
795
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700796 iconMime.animate().cancel();
797 iconThumb.animate().cancel();
798
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700799 final boolean supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700800 final boolean allowThumbnail = (state.derivedMode == MODE_GRID)
Jeff Sharkey9656a532013-09-13 13:42:19 -0700801 || MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, docMimeType);
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700802 final boolean showThumbnail = supportsThumbnail && allowThumbnail && !mSvelteRecents;
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700803
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700804 boolean cacheHit = false;
Jeff Sharkey9656a532013-09-13 13:42:19 -0700805 if (showThumbnail) {
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700806 final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700807 final Bitmap cachedResult = thumbs.get(uri);
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700808 if (cachedResult != null) {
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700809 iconThumb.setImageBitmap(cachedResult);
810 cacheHit = true;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700811 } else {
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700812 iconThumb.setImageDrawable(null);
813 final ThumbnailAsyncTask task = new ThumbnailAsyncTask(
814 uri, iconMime, iconThumb, mThumbSize);
815 iconThumb.setTag(task);
Jeff Sharkeyf63b7772013-10-01 17:57:41 -0700816 task.executeOnExecutor(ProviderExecutor.forAuthority(docAuthority));
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700817 }
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700818 }
819
820 // Always throw MIME icon into place, even when a thumbnail is being
821 // loaded in background.
822 if (cacheHit) {
823 iconMime.setAlpha(0f);
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700824 iconMime.setImageDrawable(null);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700825 iconThumb.setAlpha(1f);
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700826 } else {
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700827 iconMime.setAlpha(1f);
828 iconThumb.setAlpha(0f);
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700829 iconThumb.setImageDrawable(null);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700830 if (docIcon != 0) {
831 iconMime.setImageDrawable(
832 IconUtils.loadPackageIcon(context, docAuthority, docIcon));
833 } else {
Jeff Sharkey2ceff512013-09-18 18:03:49 -0700834 iconMime.setImageDrawable(IconUtils.loadMimeIcon(
835 context, docMimeType, docAuthority, docId, state.derivedMode));
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700836 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700837 }
838
Jeff Sharkey9656a532013-09-13 13:42:19 -0700839 boolean hasLine1 = false;
Jeff Sharkey42d26792013-09-06 13:22:09 -0700840 boolean hasLine2 = false;
841
Jeff Sharkey9656a532013-09-13 13:42:19 -0700842 final boolean hideTitle = (state.derivedMode == MODE_GRID) && mHideGridTitles;
843 if (!hideTitle) {
844 title.setText(docDisplayName);
845 hasLine1 = true;
846 }
847
848 Drawable iconDrawable = null;
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700849 if (mType == TYPE_RECENT_OPEN) {
Jeff Sharkey8b997042013-09-19 15:25:56 -0700850 // We've already had to enumerate roots before any results can
851 // be shown, so this will never block.
852 final RootInfo root = roots.getRootBlocking(docAuthority, docRootId);
Jeff Sharkey9656a532013-09-13 13:42:19 -0700853 iconDrawable = root.loadIcon(context);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700854
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700855 if (summary != null) {
856 final boolean alwaysShowSummary = getResources()
857 .getBoolean(R.bool.always_show_summary);
858 if (alwaysShowSummary) {
859 summary.setText(root.getDirectoryString());
860 summary.setVisibility(View.VISIBLE);
861 hasLine2 = true;
862 } else {
Jeff Sharkey8b997042013-09-19 15:25:56 -0700863 if (iconDrawable != null && roots.isIconUniqueBlocking(root)) {
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700864 // No summary needed if icon speaks for itself
865 summary.setVisibility(View.INVISIBLE);
866 } else {
867 summary.setText(root.getDirectoryString());
868 summary.setVisibility(View.VISIBLE);
869 summary.setTextAlignment(TextView.TEXT_ALIGNMENT_TEXT_END);
870 hasLine2 = true;
871 }
872 }
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700873 }
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700874 } else {
Jeff Sharkey9656a532013-09-13 13:42:19 -0700875 // Directories showing thumbnails in grid mode get a little icon
876 // hint to remind user they're a directory.
877 if (Document.MIME_TYPE_DIR.equals(docMimeType) && state.derivedMode == MODE_GRID
878 && showThumbnail) {
879 iconDrawable = context.getResources().getDrawable(R.drawable.ic_root_folder);
880 }
881
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700882 if (summary != null) {
883 if (docSummary != null) {
884 summary.setText(docSummary);
885 summary.setVisibility(View.VISIBLE);
886 hasLine2 = true;
887 } else {
888 summary.setVisibility(View.INVISIBLE);
889 }
Jeff Sharkeyd82b26b2013-09-02 15:07:28 -0700890 }
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700891 }
892
Jeff Sharkey9656a532013-09-13 13:42:19 -0700893 if (icon1 != null) icon1.setVisibility(View.GONE);
894 if (icon2 != null) icon2.setVisibility(View.GONE);
895
896 if (iconDrawable != null) {
897 if (hasLine1) {
898 icon1.setVisibility(View.VISIBLE);
899 icon1.setImageDrawable(iconDrawable);
900 } else {
901 icon2.setVisibility(View.VISIBLE);
902 icon2.setImageDrawable(iconDrawable);
903 }
904 }
905
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700906 if (docLastModified == -1) {
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700907 date.setText(null);
908 } else {
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700909 date.setText(formatTime(context, docLastModified));
Jeff Sharkey42d26792013-09-06 13:22:09 -0700910 hasLine2 = true;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700911 }
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700912
913 if (state.showSize) {
914 size.setVisibility(View.VISIBLE);
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700915 if (Document.MIME_TYPE_DIR.equals(docMimeType) || docSize == -1) {
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700916 size.setText(null);
917 } else {
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700918 size.setText(Formatter.formatFileSize(context, docSize));
Jeff Sharkey42d26792013-09-06 13:22:09 -0700919 hasLine2 = true;
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700920 }
921 } else {
922 size.setVisibility(View.GONE);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700923 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700924
Jeff Sharkey9656a532013-09-13 13:42:19 -0700925 if (line1 != null) {
926 line1.setVisibility(hasLine1 ? View.VISIBLE : View.GONE);
927 }
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700928 if (line2 != null) {
929 line2.setVisibility(hasLine2 ? View.VISIBLE : View.GONE);
930 }
Jeff Sharkey42d26792013-09-06 13:22:09 -0700931
Jeff Sharkey7cf49032013-09-26 10:54:16 -0700932 final boolean enabled = isDocumentEnabled(docMimeType, docFlags);
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700933 if (enabled) {
934 setEnabledRecursive(convertView, true);
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700935 iconMime.setAlpha(1f);
936 iconThumb.setAlpha(1f);
Jeff Sharkey9656a532013-09-13 13:42:19 -0700937 if (icon1 != null) icon1.setAlpha(1f);
938 if (icon2 != null) icon2.setAlpha(1f);
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700939 } else {
940 setEnabledRecursive(convertView, false);
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700941 iconMime.setAlpha(0.5f);
942 iconThumb.setAlpha(0.5f);
Jeff Sharkey9656a532013-09-13 13:42:19 -0700943 if (icon1 != null) icon1.setAlpha(0.5f);
944 if (icon2 != null) icon2.setAlpha(0.5f);
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700945 }
946
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700947 return convertView;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700948 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700949
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700950 @Override
951 public int getCount() {
Jeff Sharkey20b32272013-09-03 15:25:52 -0700952 return mCursorCount + mFooters.size();
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700953 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700954
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700955 @Override
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700956 public Cursor getItem(int position) {
Jeff Sharkey20b32272013-09-03 15:25:52 -0700957 if (position < mCursorCount) {
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700958 mCursor.moveToPosition(position);
Jeff Sharkey20b32272013-09-03 15:25:52 -0700959 return mCursor;
960 } else {
961 return null;
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700962 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700963 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700964
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700965 @Override
966 public long getItemId(int position) {
Jeff Sharkeyac9e6272013-08-31 21:27:44 -0700967 return position;
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700968 }
Jeff Sharkey20b32272013-09-03 15:25:52 -0700969
970 @Override
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700971 public int getViewTypeCount() {
972 return 4;
973 }
974
975 @Override
Jeff Sharkey20b32272013-09-03 15:25:52 -0700976 public int getItemViewType(int position) {
977 if (position < mCursorCount) {
978 return 0;
979 } else {
Jeff Sharkeyaed873d2013-09-09 16:51:06 -0700980 position -= mCursorCount;
981 return mFooters.get(position).getItemViewType();
Jeff Sharkey20b32272013-09-03 15:25:52 -0700982 }
983 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700984 }
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700985
986 private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> {
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700987 private final Uri mUri;
988 private final ImageView mIconMime;
989 private final ImageView mIconThumb;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700990 private final Point mThumbSize;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700991 private final CancellationSignal mSignal;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -0700992
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700993 public ThumbnailAsyncTask(
994 Uri uri, ImageView iconMime, ImageView iconThumb, Point thumbSize) {
995 mUri = uri;
996 mIconMime = iconMime;
997 mIconThumb = iconThumb;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700998 mThumbSize = thumbSize;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700999 mSignal = new CancellationSignal();
Jeff Sharkey8a8fb672013-05-07 12:41:33 -07001000 }
1001
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -07001002 public void reallyCancel() {
1003 cancel(false);
1004 mSignal.cancel();
Jeff Sharkey8a8fb672013-05-07 12:41:33 -07001005 }
1006
1007 @Override
1008 protected Bitmap doInBackground(Uri... params) {
Jeff Sharkeyf63b7772013-10-01 17:57:41 -07001009 if (isCancelled()) return null;
1010
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -07001011 final Context context = mIconThumb.getContext();
Jeff Sharkey3fd11772013-09-30 14:26:27 -07001012 final ContentResolver resolver = context.getContentResolver();
Jeff Sharkey8a8fb672013-05-07 12:41:33 -07001013
Jeff Sharkey3fd11772013-09-30 14:26:27 -07001014 ContentProviderClient client = null;
Jeff Sharkey8a8fb672013-05-07 12:41:33 -07001015 Bitmap result = null;
1016 try {
Jeff Sharkey3fd11772013-09-30 14:26:27 -07001017 client = DocumentsApplication.acquireUnstableProviderOrThrow(
1018 resolver, mUri.getAuthority());
1019 result = DocumentsContract.getDocumentThumbnail(client, mUri, mThumbSize, mSignal);
Jeff Sharkey8a8fb672013-05-07 12:41:33 -07001020 if (result != null) {
Jeff Sharkey873daa32013-08-18 17:38:20 -07001021 final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
1022 context, mThumbSize);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -07001023 thumbs.put(mUri, result);
Jeff Sharkey8a8fb672013-05-07 12:41:33 -07001024 }
1025 } catch (Exception e) {
Jeff Sharkey3fd11772013-09-30 14:26:27 -07001026 Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
1027 } finally {
1028 ContentProviderClient.releaseQuietly(client);
Jeff Sharkey8a8fb672013-05-07 12:41:33 -07001029 }
1030 return result;
1031 }
1032
1033 @Override
1034 protected void onPostExecute(Bitmap result) {
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -07001035 if (mIconThumb.getTag() == this && result != null) {
1036 mIconThumb.setTag(null);
1037 mIconThumb.setImageBitmap(result);
1038
Jeff Sharkey9dd02622013-09-27 16:44:11 -07001039 final float targetAlpha = mIconMime.isEnabled() ? 1f : 0.5f;
1040 mIconMime.setAlpha(targetAlpha);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -07001041 mIconMime.animate().alpha(0f).start();
1042 mIconThumb.setAlpha(0f);
Jeff Sharkey9dd02622013-09-27 16:44:11 -07001043 mIconThumb.animate().alpha(targetAlpha).start();
Jeff Sharkey8a8fb672013-05-07 12:41:33 -07001044 }
1045 }
1046 }
1047
1048 private static String formatTime(Context context, long when) {
1049 // TODO: DateUtils should make this easier
1050 Time then = new Time();
1051 then.set(when);
1052 Time now = new Time();
1053 now.setToNow();
1054
1055 int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
1056 | DateUtils.FORMAT_ABBREV_ALL;
1057
1058 if (then.year != now.year) {
1059 flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
1060 } else if (then.yearDay != now.yearDay) {
1061 flags |= DateUtils.FORMAT_SHOW_DATE;
1062 } else {
1063 flags |= DateUtils.FORMAT_SHOW_TIME;
1064 }
1065
1066 return DateUtils.formatDateTime(context, when, flags);
1067 }
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001068
1069 private String findCommonMimeType(List<String> mimeTypes) {
1070 String[] commonType = mimeTypes.get(0).split("/");
1071 if (commonType.length != 2) {
1072 return "*/*";
1073 }
1074
1075 for (int i = 1; i < mimeTypes.size(); i++) {
1076 String[] type = mimeTypes.get(i).split("/");
1077 if (type.length != 2) continue;
1078
1079 if (!commonType[1].equals(type[1])) {
1080 commonType[1] = "*";
1081 }
1082
1083 if (!commonType[0].equals(type[0])) {
1084 commonType[0] = "*";
1085 commonType[1] = "*";
1086 break;
1087 }
1088 }
1089
1090 return commonType[0] + "/" + commonType[1];
1091 }
Jeff Sharkeyaed873d2013-09-09 16:51:06 -07001092
1093 private void setEnabledRecursive(View v, boolean enabled) {
Jeff Sharkey9656a532013-09-13 13:42:19 -07001094 if (v == null) return;
Jeff Sharkeyaed873d2013-09-09 16:51:06 -07001095 if (v.isEnabled() == enabled) return;
1096 v.setEnabled(enabled);
1097
1098 if (v instanceof ViewGroup) {
1099 final ViewGroup vg = (ViewGroup) v;
1100 for (int i = vg.getChildCount() - 1; i >= 0; i--) {
1101 setEnabledRecursive(vg.getChildAt(i), enabled);
1102 }
1103 }
1104 }
Jeff Sharkey7cf49032013-09-26 10:54:16 -07001105
1106 private boolean isDocumentEnabled(String docMimeType, int docFlags) {
1107 final State state = getDisplayState(DirectoryFragment.this);
1108
Jeff Sharkey7cf49032013-09-26 10:54:16 -07001109 // Directories are always enabled
1110 if (Document.MIME_TYPE_DIR.equals(docMimeType)) {
1111 return true;
1112 }
1113
Jeff Sharkey783ebc22013-09-26 19:42:52 -07001114 // Read-only files are disabled when creating
1115 if (state.action == ACTION_CREATE && (docFlags & Document.FLAG_SUPPORTS_WRITE) == 0) {
1116 return false;
1117 }
1118
Jeff Sharkey7cf49032013-09-26 10:54:16 -07001119 return MimePredicate.mimeMatches(state.acceptMimes, docMimeType);
1120 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -07001121}