blob: 457bb1916b00818b9667a2fdd1e62dbfa996faf3 [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 Sharkeyc8ae7a52013-09-18 16:26:49 -070019import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
20import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
21import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
22import static com.android.documentsui.DirectoryFragment.ANIM_UP;
Jeff Sharkeyb3620442013-09-01 18:41:04 -070023import static com.android.documentsui.DocumentsActivity.State.ACTION_CREATE;
24import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
25import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
26import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN;
27import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
28import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
Jeff Sharkey873daa32013-08-18 17:38:20 -070029
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070030import android.app.ActionBar;
31import android.app.ActionBar.OnNavigationListener;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070032import android.app.Activity;
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -070033import android.app.Fragment;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070034import android.app.FragmentManager;
Jeff Sharkey873daa32013-08-18 17:38:20 -070035import android.content.ActivityNotFoundException;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070036import android.content.ClipData;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070037import android.content.ComponentName;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070038import android.content.ContentResolver;
Jeff Sharkeyf7adc932013-07-30 17:08:39 -070039import android.content.ContentValues;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070040import android.content.Intent;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070041import android.content.pm.ResolveInfo;
Jeff Sharkey7d58fc62013-09-12 16:25:02 -070042import android.content.res.Resources;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070043import android.database.Cursor;
Jeff Sharkey7d58fc62013-09-12 16:25:02 -070044import android.graphics.Point;
Jeff Sharkey46165b52013-07-31 20:53:22 -070045import android.graphics.drawable.ColorDrawable;
Jeff Sharkey7d58fc62013-09-12 16:25:02 -070046import android.graphics.drawable.Drawable;
47import android.graphics.drawable.InsetDrawable;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070048import android.net.Uri;
Jeff Sharkey8b997042013-09-19 15:25:56 -070049import android.os.AsyncTask;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070050import android.os.Bundle;
Jeff Sharkeyb3620442013-09-01 18:41:04 -070051import android.os.Parcel;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -070052import android.os.Parcelable;
Jeff Sharkeyf339f252013-08-15 16:17:41 -070053import android.provider.DocumentsContract;
Jeff Sharkey0e8c8712013-09-12 21:59:06 -070054import android.provider.DocumentsContract.Root;
Jeff Sharkey46165b52013-07-31 20:53:22 -070055import android.support.v4.app.ActionBarDrawerToggle;
56import android.support.v4.view.GravityCompat;
57import android.support.v4.widget.DrawerLayout;
58import android.support.v4.widget.DrawerLayout.DrawerListener;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070059import android.util.Log;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -070060import android.util.SparseArray;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070061import android.view.LayoutInflater;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070062import android.view.Menu;
63import android.view.MenuItem;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070064import android.view.MenuItem.OnActionExpandListener;
Jeff Sharkey7d58fc62013-09-12 16:25:02 -070065import android.view.MotionEvent;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070066import android.view.View;
Jeff Sharkey7d58fc62013-09-12 16:25:02 -070067import android.view.View.OnTouchListener;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070068import android.view.ViewGroup;
Jeff Sharkey7d58fc62013-09-12 16:25:02 -070069import android.view.WindowManager;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070070import android.widget.BaseAdapter;
Jeff Sharkey42d26792013-09-06 13:22:09 -070071import android.widget.ImageView;
Jeff Sharkey46165b52013-07-31 20:53:22 -070072import android.widget.SearchView;
73import android.widget.SearchView.OnQueryTextListener;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -070074import android.widget.TextView;
Jeff Sharkey78278212013-08-01 11:01:47 -070075import android.widget.Toast;
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070076
Jeff Sharkeya4d1f222013-09-07 14:45:03 -070077import com.android.documentsui.RecentsProvider.RecentColumns;
78import com.android.documentsui.RecentsProvider.ResumeColumns;
Jeff Sharkey724deeb2013-08-31 15:02:20 -070079import com.android.documentsui.model.DocumentInfo;
Jeff Sharkey2e694f82013-08-06 16:26:14 -070080import com.android.documentsui.model.DocumentStack;
Jeff Sharkeyb3620442013-09-01 18:41:04 -070081import com.android.documentsui.model.DurableUtils;
Jeff Sharkey724deeb2013-08-31 15:02:20 -070082import com.android.documentsui.model.RootInfo;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -070083import com.google.common.collect.Maps;
Jeff Sharkeyf7adc932013-07-30 17:08:39 -070084
Jeff Sharkey4c5fe522013-09-03 14:17:06 -070085import libcore.io.IoUtils;
86
Jeff Sharkey744dbbd2013-08-07 18:33:33 -070087import java.io.FileNotFoundException;
Jeff Sharkeyb3620442013-09-01 18:41:04 -070088import java.io.IOException;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070089import java.util.Arrays;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -070090import java.util.HashMap;
Jeff Sharkey8b997042013-09-19 15:25:56 -070091import java.util.Collection;
Jeff Sharkeyc317af82013-07-01 16:56:54 -070092import java.util.List;
93
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070094public class DocumentsActivity extends Activity {
Jeff Sharkeya5defe32013-08-05 17:56:48 -070095 public static final String TAG = "Documents";
Jeff Sharkeye22d02e2013-04-26 16:54:55 -070096
Jeff Sharkeyb3620442013-09-01 18:41:04 -070097 private static final String EXTRA_STATE = "state";
Jeff Sharkey3c28b792013-07-01 17:22:02 -070098
Jeff Sharkey7d58fc62013-09-12 16:25:02 -070099 private boolean mShowAsDialog;
100
101 private SearchView mSearchView;
102
103 private DrawerLayout mDrawerLayout;
104 private ActionBarDrawerToggle mDrawerToggle;
105 private View mRootsContainer;
106
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700107 private DirectoryContainerView mDirectoryContainer;
108
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700109 private boolean mIgnoreNextNavigation;
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700110 private boolean mIgnoreNextClose;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700111 private boolean mIgnoreNextCollapse;
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700112
Jeff Sharkey873daa32013-08-18 17:38:20 -0700113 private RootsCache mRoots;
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700114 private State mState;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700115
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700116 @Override
117 public void onCreate(Bundle icicle) {
118 super.onCreate(icicle);
119
Jeff Sharkey873daa32013-08-18 17:38:20 -0700120 mRoots = DocumentsApplication.getRootsCache(this);
121
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700122 setResult(Activity.RESULT_CANCELED);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700123 setContentView(R.layout.activity);
124
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700125 final Resources res = getResources();
126 mShowAsDialog = res.getBoolean(R.bool.show_as_dialog);
Jeff Sharkey76112212013-08-06 11:26:10 -0700127
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700128 if (mShowAsDialog) {
129 // backgroundDimAmount from theme isn't applied; do it manually
130 final WindowManager.LayoutParams a = getWindow().getAttributes();
131 a.dimAmount = 0.6f;
132 getWindow().setAttributes(a);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700133
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700134 getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
135 getWindow().setFlags(~0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700136
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700137 // Inset ourselves to look like a dialog
138 final Point size = new Point();
139 getWindowManager().getDefaultDisplay().getSize(size);
140
141 final int width = (int) res.getFraction(R.dimen.dialog_width, size.x, size.x);
142 final int height = (int) res.getFraction(R.dimen.dialog_height, size.y, size.y);
143 final int insetX = (size.x - width) / 2;
144 final int insetY = (size.y - height) / 2;
145
146 final Drawable before = getWindow().getDecorView().getBackground();
147 final Drawable after = new InsetDrawable(before, insetX, insetY, insetX, insetY);
148 getWindow().getDecorView().setBackground(after);
149
150 // Dismiss when touch down in the dimmed inset area
151 getWindow().getDecorView().setOnTouchListener(new OnTouchListener() {
152 @Override
153 public boolean onTouch(View v, MotionEvent event) {
154 if (event.getAction() == MotionEvent.ACTION_DOWN) {
155 final float x = event.getX();
156 final float y = event.getY();
157 if (x < insetX || x > v.getWidth() - insetX || y < insetY
158 || y > v.getHeight() - insetY) {
159 finish();
160 return true;
161 }
162 }
163 return false;
164 }
165 });
166
167 } else {
168 // Non-dialog means we have a drawer
169 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
170
171 mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
172 R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
173
174 mDrawerLayout.setDrawerListener(mDrawerListener);
175 mDrawerLayout.setDrawerShadow(R.drawable.ic_drawer_shadow, GravityCompat.START);
176
177 mRootsContainer = findViewById(R.id.container_roots);
178 }
Jeff Sharkey46165b52013-07-31 20:53:22 -0700179
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700180 mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
181
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700182 if (icicle != null) {
183 mState = icicle.getParcelable(EXTRA_STATE);
184 } else {
185 buildDefaultState();
186 }
Jeff Sharkey5b535922013-08-02 15:55:26 -0700187
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700188 // Hide roots when we're managing a specific root
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700189 if (mState.action == ACTION_MANAGE) {
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700190 if (mShowAsDialog) {
191 findViewById(R.id.dialog_roots).setVisibility(View.GONE);
192 } else {
193 mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
194 }
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700195 }
196
197 if (mState.action == ACTION_CREATE) {
198 final String mimeType = getIntent().getType();
199 final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
200 SaveFragment.show(getFragmentManager(), mimeType, title);
201 }
202
203 if (mState.action == ACTION_GET_CONTENT) {
204 final Intent moreApps = new Intent(getIntent());
205 moreApps.setComponent(null);
206 moreApps.setPackage(null);
207 RootsFragment.show(getFragmentManager(), moreApps);
208 } else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE) {
209 RootsFragment.show(getFragmentManager(), null);
210 }
211
Jeff Sharkey8b997042013-09-19 15:25:56 -0700212 if (!mState.restored) {
213 if (mState.action == ACTION_MANAGE) {
214 final Uri rootUri = getIntent().getData();
215 new RestoreRootTask(rootUri).execute();
216 } else {
217 new RestoreStackTask().execute();
218 }
219 } else {
220 onCurrentDirectoryChanged(ANIM_NONE);
221 }
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700222 }
223
224 private void buildDefaultState() {
225 mState = new State();
226
227 final Intent intent = getIntent();
228 final String action = intent.getAction();
229 if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
230 mState.action = ACTION_OPEN;
231 } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
232 mState.action = ACTION_CREATE;
233 } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
234 mState.action = ACTION_GET_CONTENT;
Jeff Sharkey91a101c2013-09-05 17:14:14 -0700235 } else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700236 mState.action = ACTION_MANAGE;
237 }
238
239 if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
240 mState.allowMultiple = intent.getBooleanExtra(
241 Intent.EXTRA_ALLOW_MULTIPLE, false);
242 }
243
244 if (mState.action == ACTION_MANAGE) {
245 mState.acceptMimes = new String[] { "*/*" };
246 mState.allowMultiple = true;
247 } else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
248 mState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
249 } else {
250 mState.acceptMimes = new String[] { intent.getType() };
251 }
252
253 mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700254 mState.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(this);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700255 }
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700256
Jeff Sharkey8b997042013-09-19 15:25:56 -0700257 private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
258 private Uri mRootUri;
259
260 public RestoreRootTask(Uri rootUri) {
261 mRootUri = rootUri;
262 }
263
264 @Override
265 protected RootInfo doInBackground(Void... params) {
266 final String rootId = DocumentsContract.getRootId(mRootUri);
267 return mRoots.getRootOneshot(mRootUri.getAuthority(), rootId);
268 }
269
270 @Override
271 protected void onPostExecute(RootInfo root) {
272 if (isDestroyed()) return;
273 mState.restored = true;
274
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700275 if (root != null) {
276 onRootPicked(root, true);
277 } else {
Jeff Sharkey8b997042013-09-19 15:25:56 -0700278 Log.w(TAG, "Failed to find root: " + mRootUri);
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700279 finish();
Jeff Sharkey5b535922013-08-02 15:55:26 -0700280 }
Jeff Sharkey8b997042013-09-19 15:25:56 -0700281 }
282 }
Jeff Sharkey5b535922013-08-02 15:55:26 -0700283
Jeff Sharkey8b997042013-09-19 15:25:56 -0700284 private class RestoreStackTask extends AsyncTask<Void, Void, Void> {
285 private volatile boolean mRestoredStack;
286
287 @Override
288 protected Void doInBackground(Void... params) {
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700289 // Restore last stack for calling package
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700290 final String packageName = getCallingPackage();
291 final Cursor cursor = getContentResolver()
292 .query(RecentsProvider.buildResume(packageName), null, null, null, null);
293 try {
294 if (cursor.moveToFirst()) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700295 final byte[] rawStack = cursor.getBlob(
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700296 cursor.getColumnIndex(ResumeColumns.STACK));
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700297 DurableUtils.readFromArray(rawStack, mState.stack);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700298 mRestoredStack = true;
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700299 }
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700300 } catch (IOException e) {
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700301 Log.w(TAG, "Failed to resume", e);
302 } finally {
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700303 IoUtils.closeQuietly(cursor);
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700304 }
305
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700306 // If restored root isn't valid, fall back to recents
307 final RootInfo root = getCurrentRoot();
Jeff Sharkey8b997042013-09-19 15:25:56 -0700308 final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700309 if (!matchingRoots.contains(root)) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700310 mState.stack.reset();
Jeff Sharkey8b997042013-09-19 15:25:56 -0700311 mRestoredStack = false;
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700312 }
313
Jeff Sharkey8b997042013-09-19 15:25:56 -0700314 return null;
315 }
316
317 @Override
318 protected void onPostExecute(Void result) {
319 if (isDestroyed()) return;
320 mState.restored = true;
321
Jeff Sharkey9656a532013-09-13 13:42:19 -0700322 // Only open drawer when not restoring stack, and when not showing
323 // visual content.
Jeff Sharkey8b997042013-09-19 15:25:56 -0700324 if (!mRestoredStack
Jeff Sharkey9656a532013-09-13 13:42:19 -0700325 && !MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700326 setRootsDrawerOpen(true);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700327 }
Jeff Sharkey8b997042013-09-19 15:25:56 -0700328
329 onCurrentDirectoryChanged(ANIM_NONE);
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700330 }
331 }
332
333 @Override
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700334 public void onResume() {
335 super.onResume();
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700336
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700337 if (mState.action == ACTION_MANAGE) {
338 mState.showSize = true;
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700339 } else {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700340 mState.showSize = SettingsActivity.getDisplayFileSize(this);
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700341 }
Jeff Sharkey46165b52013-07-31 20:53:22 -0700342 }
343
344 private DrawerListener mDrawerListener = new DrawerListener() {
345 @Override
346 public void onDrawerSlide(View drawerView, float slideOffset) {
347 mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
348 }
349
350 @Override
351 public void onDrawerOpened(View drawerView) {
352 mDrawerToggle.onDrawerOpened(drawerView);
353 updateActionBar();
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700354 invalidateOptionsMenu();
Jeff Sharkey46165b52013-07-31 20:53:22 -0700355 }
356
357 @Override
358 public void onDrawerClosed(View drawerView) {
359 mDrawerToggle.onDrawerClosed(drawerView);
360 updateActionBar();
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700361 invalidateOptionsMenu();
Jeff Sharkey46165b52013-07-31 20:53:22 -0700362 }
363
364 @Override
365 public void onDrawerStateChanged(int newState) {
366 mDrawerToggle.onDrawerStateChanged(newState);
367 }
368 };
369
370 @Override
371 protected void onPostCreate(Bundle savedInstanceState) {
372 super.onPostCreate(savedInstanceState);
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700373 if (mDrawerToggle != null) {
374 mDrawerToggle.syncState();
375 }
376 }
377
378 public void setRootsDrawerOpen(boolean open) {
379 if (!mShowAsDialog) {
380 if (open) {
381 mDrawerLayout.openDrawer(mRootsContainer);
382 } else {
383 mDrawerLayout.closeDrawer(mRootsContainer);
384 }
385 }
386 }
387
388 private boolean isRootsDrawerOpen() {
389 if (mShowAsDialog) {
390 return false;
391 } else {
392 return mDrawerLayout.isDrawerOpen(mRootsContainer);
393 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700394 }
395
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700396 public void updateActionBar() {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700397 final ActionBar actionBar = getActionBar();
398
Jeff Sharkey46165b52013-07-31 20:53:22 -0700399 actionBar.setDisplayShowHomeEnabled(true);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700400
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700401 final boolean showIndicator = !mShowAsDialog && (mState.action != ACTION_MANAGE);
402 actionBar.setDisplayHomeAsUpEnabled(showIndicator);
403 if (mDrawerToggle != null) {
404 mDrawerToggle.setDrawerIndicatorEnabled(showIndicator);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700405 }
406
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700407 if (isRootsDrawerOpen()) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700408 actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700409 actionBar.setIcon(new ColorDrawable());
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700410
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700411 if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700412 actionBar.setTitle(R.string.title_open);
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700413 } else if (mState.action == ACTION_CREATE) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700414 actionBar.setTitle(R.string.title_save);
415 }
Jeff Sharkey46165b52013-07-31 20:53:22 -0700416 } else {
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700417 final RootInfo root = getCurrentRoot();
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700418 actionBar.setIcon(root != null ? root.loadIcon(this) : null);
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700419
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700420 if (mState.stack.size() <= 1) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700421 actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
422 actionBar.setTitle(root.title);
423 } else {
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700424 mIgnoreNextNavigation = true;
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700425 actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
426 actionBar.setTitle(null);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700427 actionBar.setListNavigationCallbacks(mStackAdapter, mStackListener);
428 actionBar.setSelectedNavigationItem(mStackAdapter.getCount() - 1);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700429 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700430 }
431 }
432
433 @Override
434 public boolean onCreateOptionsMenu(Menu menu) {
435 super.onCreateOptionsMenu(menu);
436 getMenuInflater().inflate(R.menu.activity, menu);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700437
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700438 // Actions are always visible when showing as dialog
439 if (mShowAsDialog) {
440 for (int i = 0; i < menu.size(); i++) {
441 menu.getItem(i).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
442 }
443 }
444
Jeff Sharkey46165b52013-07-31 20:53:22 -0700445 final MenuItem searchMenu = menu.findItem(R.id.menu_search);
446 mSearchView = (SearchView) searchMenu.getActionView();
447 mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
448 @Override
449 public boolean onQueryTextSubmit(String query) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700450 mState.currentSearch = query;
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700451 mSearchView.clearFocus();
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700452 onCurrentDirectoryChanged(ANIM_NONE);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700453 return true;
454 }
455
456 @Override
457 public boolean onQueryTextChange(String newText) {
458 return false;
459 }
460 });
461
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700462 searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700463 @Override
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700464 public boolean onMenuItemActionExpand(MenuItem item) {
465 return true;
466 }
467
468 @Override
469 public boolean onMenuItemActionCollapse(MenuItem item) {
470 if (mIgnoreNextCollapse) {
471 mIgnoreNextCollapse = false;
472 return true;
473 }
474
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700475 mState.currentSearch = null;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700476 onCurrentDirectoryChanged(ANIM_NONE);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700477 return true;
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700478 }
479 });
480
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700481 mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
482 @Override
483 public boolean onClose() {
484 if (mIgnoreNextClose) {
485 mIgnoreNextClose = false;
486 return false;
487 }
488
489 mState.currentSearch = null;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700490 onCurrentDirectoryChanged(ANIM_NONE);
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700491 return false;
492 }
493 });
494
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700495 return true;
496 }
497
498 @Override
499 public boolean onPrepareOptionsMenu(Menu menu) {
500 super.onPrepareOptionsMenu(menu);
501
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700502 final FragmentManager fm = getFragmentManager();
Jeff Sharkey0e8c8712013-09-12 21:59:06 -0700503
504 final RootInfo root = getCurrentRoot();
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700505 final DocumentInfo cwd = getCurrentDirectory();
Jeff Sharkey5b535922013-08-02 15:55:26 -0700506
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700507 final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700508 final MenuItem search = menu.findItem(R.id.menu_search);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700509 final MenuItem sort = menu.findItem(R.id.menu_sort);
510 final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
511 final MenuItem grid = menu.findItem(R.id.menu_grid);
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700512 final MenuItem list = menu.findItem(R.id.menu_list);
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700513 final MenuItem settings = menu.findItem(R.id.menu_settings);
Jeff Sharkey5b535922013-08-02 15:55:26 -0700514
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700515 // Open drawer means we hide most actions
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700516 if (isRootsDrawerOpen()) {
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700517 createDir.setVisible(false);
518 search.setVisible(false);
519 sort.setVisible(false);
520 grid.setVisible(false);
521 list.setVisible(false);
522 mIgnoreNextCollapse = true;
523 search.collapseActionView();
524 return true;
525 }
526
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700527 sort.setVisible(cwd != null);
528 grid.setVisible(mState.derivedMode != MODE_GRID);
529 list.setVisible(mState.derivedMode != MODE_LIST);
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700530
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700531 if (mState.currentSearch != null) {
532 // Search uses backend ranking; no sorting
533 sort.setVisible(false);
534
535 search.expandActionView();
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700536
537 mSearchView.setIconified(false);
538 mSearchView.clearFocus();
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700539 mSearchView.setQuery(mState.currentSearch, false);
540 } else {
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700541 mIgnoreNextClose = true;
542 mSearchView.setIconified(true);
543 mSearchView.clearFocus();
544
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700545 mIgnoreNextCollapse = true;
546 search.collapseActionView();
547 }
548
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700549 // Only sort by size when visible
550 sortSize.setVisible(mState.showSize);
551
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700552 final boolean searchVisible;
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700553 if (mState.action == ACTION_CREATE) {
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700554 createDir.setVisible(cwd != null && cwd.isCreateSupported());
555 searchVisible = false;
556
557 // No display options in recent directories
558 if (cwd == null) {
559 grid.setVisible(false);
560 list.setVisible(false);
561 }
562
Jeff Sharkey5b535922013-08-02 15:55:26 -0700563 SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700564 } else {
565 createDir.setVisible(false);
Jeff Sharkey0e8c8712013-09-12 21:59:06 -0700566
567 searchVisible = root != null
568 && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0);
Jeff Sharkey5b535922013-08-02 15:55:26 -0700569 }
Jeff Sharkey46165b52013-07-31 20:53:22 -0700570
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700571 // TODO: close any search in-progress when hiding
572 search.setVisible(searchVisible);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700573
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700574 settings.setVisible(mState.action != ACTION_MANAGE);
Jeff Sharkeyf339f252013-08-15 16:17:41 -0700575
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700576 return true;
577 }
578
579 @Override
580 public boolean onOptionsItemSelected(MenuItem item) {
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700581 if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
Jeff Sharkey46165b52013-07-31 20:53:22 -0700582 return true;
583 }
584
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700585 final int id = item.getItemId();
586 if (id == android.R.id.home) {
Jeff Sharkey5b535922013-08-02 15:55:26 -0700587 onBackPressed();
Jeff Sharkey46165b52013-07-31 20:53:22 -0700588 return true;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700589 } else if (id == R.id.menu_create_dir) {
Jeff Sharkeyf7adc932013-07-30 17:08:39 -0700590 CreateDirectoryFragment.show(getFragmentManager());
Jeff Sharkey46165b52013-07-31 20:53:22 -0700591 return true;
592 } else if (id == R.id.menu_search) {
593 return false;
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700594 } else if (id == R.id.menu_sort_name) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700595 setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700596 return true;
597 } else if (id == R.id.menu_sort_date) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700598 setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700599 return true;
600 } else if (id == R.id.menu_sort_size) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700601 setUserSortOrder(State.SORT_ORDER_SIZE);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700602 return true;
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700603 } else if (id == R.id.menu_grid) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700604 setUserMode(State.MODE_GRID);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700605 return true;
606 } else if (id == R.id.menu_list) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700607 setUserMode(State.MODE_LIST);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700608 return true;
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700609 } else if (id == R.id.menu_settings) {
610 startActivity(new Intent(this, SettingsActivity.class));
611 return true;
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700612 } else {
613 return super.onOptionsItemSelected(item);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700614 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700615 }
616
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700617 /**
618 * Update UI to reflect internal state changes not from user.
619 */
620 public void onStateChanged() {
621 invalidateOptionsMenu();
622 }
623
624 /**
625 * Set state sort order based on explicit user action.
626 */
627 private void setUserSortOrder(int sortOrder) {
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700628 mState.userSortOrder = sortOrder;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700629 DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700630 }
631
632 /**
633 * Set state mode based on explicit user action.
634 */
635 private void setUserMode(int mode) {
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700636 mState.userMode = mode;
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700637 DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700638 }
639
Jeff Sharkey5b535922013-08-02 15:55:26 -0700640 @Override
641 public void onBackPressed() {
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700642 if (!mState.stackTouched) {
643 super.onBackPressed();
644 return;
645 }
646
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700647 final int size = mState.stack.size();
Jeff Sharkey5b535922013-08-02 15:55:26 -0700648 if (size > 1) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700649 mState.stack.pop();
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700650 onCurrentDirectoryChanged(ANIM_UP);
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700651 } else if (size == 1 && !isRootsDrawerOpen()) {
Jeff Sharkey5b535922013-08-02 15:55:26 -0700652 // TODO: open root drawer once we can capture back key
653 super.onBackPressed();
654 } else {
655 super.onBackPressed();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700656 }
Jeff Sharkey5b535922013-08-02 15:55:26 -0700657 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700658
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700659 @Override
660 protected void onSaveInstanceState(Bundle state) {
661 super.onSaveInstanceState(state);
662 state.putParcelable(EXTRA_STATE, mState);
663 }
664
665 @Override
666 protected void onRestoreInstanceState(Bundle state) {
667 super.onRestoreInstanceState(state);
668 updateActionBar();
669 }
670
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700671 private BaseAdapter mStackAdapter = new BaseAdapter() {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700672 @Override
673 public int getCount() {
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700674 return mState.stack.size();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700675 }
676
677 @Override
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700678 public DocumentInfo getItem(int position) {
679 return mState.stack.get(mState.stack.size() - position - 1);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700680 }
681
682 @Override
683 public long getItemId(int position) {
Jeff Sharkey46165b52013-07-31 20:53:22 -0700684 return position;
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700685 }
686
687 @Override
688 public View getView(int position, View convertView, ViewGroup parent) {
689 if (convertView == null) {
690 convertView = LayoutInflater.from(parent.getContext())
Jeff Sharkey46165b52013-07-31 20:53:22 -0700691 .inflate(R.layout.item_title, parent, false);
692 }
693
694 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700695 final DocumentInfo doc = getItem(position);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700696
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700697 if (position == 0) {
698 final RootInfo root = getCurrentRoot();
699 title.setText(root.title);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700700 } else {
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700701 title.setText(doc.displayName);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700702 }
703
Jeff Sharkey42d26792013-09-06 13:22:09 -0700704 // No padding when shown in actionbar
705 convertView.setPadding(0, 0, 0, 0);
Jeff Sharkey46165b52013-07-31 20:53:22 -0700706 return convertView;
707 }
708
709 @Override
710 public View getDropDownView(int position, View convertView, ViewGroup parent) {
711 if (convertView == null) {
712 convertView = LayoutInflater.from(parent.getContext())
Jeff Sharkey42d26792013-09-06 13:22:09 -0700713 .inflate(R.layout.item_title, parent, false);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700714 }
715
Jeff Sharkey42d26792013-09-06 13:22:09 -0700716 final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
717 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700718 final DocumentInfo doc = getItem(position);
719
720 if (position == 0) {
721 final RootInfo root = getCurrentRoot();
Jeff Sharkey42d26792013-09-06 13:22:09 -0700722 title.setText(root.title);
723 subdir.setVisibility(View.GONE);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700724 } else {
Jeff Sharkey42d26792013-09-06 13:22:09 -0700725 title.setText(doc.displayName);
726 subdir.setVisibility(View.VISIBLE);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700727 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700728
729 return convertView;
730 }
731 };
732
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700733 private OnNavigationListener mStackListener = new OnNavigationListener() {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700734 @Override
735 public boolean onNavigationItemSelected(int itemPosition, long itemId) {
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700736 if (mIgnoreNextNavigation) {
737 mIgnoreNextNavigation = false;
738 return false;
739 }
740
741 while (mState.stack.size() > itemPosition + 1) {
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700742 mState.stackTouched = true;
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700743 mState.stack.pop();
744 }
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700745 onCurrentDirectoryChanged(ANIM_UP);
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700746 return true;
747 }
748 };
749
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700750 public RootInfo getCurrentRoot() {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700751 if (mState.stack.root != null) {
752 return mState.stack.root;
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700753 } else {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700754 return mRoots.getRecentsRoot();
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700755 }
Jeff Sharkey5b535922013-08-02 15:55:26 -0700756 }
757
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700758 public DocumentInfo getCurrentDirectory() {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700759 return mState.stack.peek();
Jeff Sharkey5b535922013-08-02 15:55:26 -0700760 }
761
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700762 public State getDisplayState() {
763 return mState;
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700764 }
765
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700766 private void onCurrentDirectoryChanged(int anim) {
Jeff Sharkey5b535922013-08-02 15:55:26 -0700767 final FragmentManager fm = getFragmentManager();
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700768 final RootInfo root = getCurrentRoot();
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700769 final DocumentInfo cwd = getCurrentDirectory();
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700770
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700771 mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
772
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700773 if (cwd == null) {
774 // No directory means recents
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700775 if (mState.action == ACTION_CREATE) {
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700776 RecentsCreateFragment.show(fm);
777 } else {
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700778 DirectoryFragment.showRecentsOpen(fm, anim);
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700779
780 // Start recents in relevant mode
781 final boolean acceptImages = MimePredicate.mimeMatches(
782 "image/*", mState.acceptMimes);
783 mState.userMode = acceptImages ? MODE_GRID : MODE_LIST;
784 mState.derivedMode = mState.userMode;
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700785 }
786 } else {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700787 if (mState.currentSearch != null) {
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700788 // Ongoing search
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700789 DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700790 } else {
791 // Normal boring directory
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700792 DirectoryFragment.showNormal(fm, root, cwd, anim);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700793 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700794 }
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700795
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700796 // Forget any replacement target
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700797 if (mState.action == ACTION_CREATE) {
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700798 final SaveFragment save = SaveFragment.get(fm);
799 if (save != null) {
800 save.setReplaceTarget(null);
801 }
802 }
803
Jeff Sharkey42d26792013-09-06 13:22:09 -0700804 final RootsFragment roots = RootsFragment.get(fm);
805 if (roots != null) {
806 roots.onCurrentRootChanged();
807 }
808
Jeff Sharkey5b535922013-08-02 15:55:26 -0700809 updateActionBar();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700810 invalidateOptionsMenu();
Jeff Sharkey5b535922013-08-02 15:55:26 -0700811 dumpStack();
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700812 }
813
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700814 public void onStackPicked(DocumentStack stack) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700815 mState.stack = stack;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700816 mState.stackTouched = true;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700817 onCurrentDirectoryChanged(ANIM_SIDE);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700818 }
819
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700820 public void onRootPicked(RootInfo root, boolean closeDrawer) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700821 // Clear entire backstack and start in new root
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700822 mState.stack.root = root;
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700823 mState.stack.clear();
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700824 mState.stackTouched = true;
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700825
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700826 if (!mRoots.isRecentsRoot(root)) {
Jeff Sharkey744dbbd2013-08-07 18:33:33 -0700827 try {
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700828 final Uri uri = DocumentsContract.buildDocumentUri(root.authority, root.documentId);
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700829 final DocumentInfo doc = DocumentInfo.fromUri(getContentResolver(), uri);
830 mState.stack.push(doc);
831 mState.stackTouched = true;
832 onCurrentDirectoryChanged(ANIM_SIDE);
Jeff Sharkey744dbbd2013-08-07 18:33:33 -0700833 } catch (FileNotFoundException e) {
834 }
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700835 } else {
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700836 onCurrentDirectoryChanged(ANIM_SIDE);
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700837 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700838
839 if (closeDrawer) {
Jeff Sharkey7d58fc62013-09-12 16:25:02 -0700840 setRootsDrawerOpen(false);
Jeff Sharkey76112212013-08-06 11:26:10 -0700841 }
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700842 }
843
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700844 public void onAppPicked(ResolveInfo info) {
845 final Intent intent = new Intent(getIntent());
846 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
847 intent.setComponent(new ComponentName(
848 info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
849 startActivity(intent);
850 finish();
851 }
852
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700853 public void onDocumentPicked(DocumentInfo doc) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700854 final FragmentManager fm = getFragmentManager();
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700855 if (doc.isDirectory()) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700856 mState.stack.push(doc);
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700857 mState.stackTouched = true;
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700858 onCurrentDirectoryChanged(ANIM_DOWN);
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700859 } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700860 // Explicit file picked, return
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700861 onFinished(doc.derivedUri);
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700862 } else if (mState.action == ACTION_CREATE) {
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700863 // Replace selected file
864 SaveFragment.get(fm).setReplaceTarget(doc);
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700865 } else if (mState.action == ACTION_MANAGE) {
Jeff Sharkey91a101c2013-09-05 17:14:14 -0700866 // First try managing the document; we expect manager to filter
867 // based on authority, so we don't grant.
868 final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700869 manage.setData(doc.derivedUri);
Jeff Sharkey91a101c2013-09-05 17:14:14 -0700870
Jeff Sharkey873daa32013-08-18 17:38:20 -0700871 try {
Jeff Sharkey91a101c2013-09-05 17:14:14 -0700872 startActivity(manage);
Jeff Sharkey873daa32013-08-18 17:38:20 -0700873 } catch (ActivityNotFoundException ex) {
Jeff Sharkey91a101c2013-09-05 17:14:14 -0700874 // Fall back to viewing
875 final Intent view = new Intent(Intent.ACTION_VIEW);
876 view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700877 view.setData(doc.derivedUri);
Jeff Sharkey91a101c2013-09-05 17:14:14 -0700878
879 try {
880 startActivity(view);
881 } catch (ActivityNotFoundException ex2) {
882 Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
883 }
Jeff Sharkey873daa32013-08-18 17:38:20 -0700884 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700885 }
886 }
887
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700888 public void onDocumentsPicked(List<DocumentInfo> docs) {
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700889 if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700890 final int size = docs.size();
891 final Uri[] uris = new Uri[size];
892 for (int i = 0; i < size; i++) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700893 uris[i] = docs.get(i).derivedUri;
Jeff Sharkey873daa32013-08-18 17:38:20 -0700894 }
895 onFinished(uris);
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700896 }
Jeff Sharkey09c10bf2013-06-30 20:02:59 -0700897 }
898
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700899 public void onSaveRequested(DocumentInfo replaceTarget) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700900 onFinished(replaceTarget.derivedUri);
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700901 }
Jeff Sharkey50d35622013-08-02 10:33:21 -0700902
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700903 public void onSaveRequested(String mimeType, String displayName) {
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700904 final DocumentInfo cwd = getCurrentDirectory();
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700905
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700906 final Uri childUri = DocumentsContract.createDocument(
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700907 getContentResolver(), cwd.derivedUri, mimeType, displayName);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700908 if (childUri != null) {
Jeff Sharkey5b535922013-08-02 15:55:26 -0700909 onFinished(childUri);
Jeff Sharkey4c5fe522013-09-03 14:17:06 -0700910 } else {
Jeff Sharkey78278212013-08-01 11:01:47 -0700911 Toast.makeText(this, R.string.save_error, Toast.LENGTH_SHORT).show();
912 }
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700913 }
914
915 private void onFinished(Uri... uris) {
916 Log.d(TAG, "onFinished() " + Arrays.toString(uris));
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700917
Jeff Sharkey5b535922013-08-02 15:55:26 -0700918 final ContentResolver resolver = getContentResolver();
919 final ContentValues values = new ContentValues();
920
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700921 final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
922 if (mState.action == ACTION_CREATE) {
Jeff Sharkey5b535922013-08-02 15:55:26 -0700923 // Remember stack for last create
924 values.clear();
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700925 values.put(RecentColumns.STACK, rawStack);
926 resolver.insert(RecentsProvider.buildRecent(), values);
Jeff Sharkey5b535922013-08-02 15:55:26 -0700927 }
928
929 // Remember location for next app launch
930 final String packageName = getCallingPackage();
931 values.clear();
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700932 values.put(ResumeColumns.STACK, rawStack);
Jeff Sharkey5b535922013-08-02 15:55:26 -0700933 resolver.insert(RecentsProvider.buildResume(packageName), values);
934
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700935 final Intent intent = new Intent();
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700936 if (uris.length == 1) {
937 intent.setData(uris[0]);
938 } else if (uris.length > 1) {
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700939 final ClipData clipData = new ClipData(
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700940 null, mState.acceptMimes, new ClipData.Item(uris[0]));
Jeff Sharkeyc317af82013-07-01 16:56:54 -0700941 for (int i = 1; i < uris.length; i++) {
942 clipData.addItem(new ClipData.Item(uris[i]));
943 }
944 intent.setClipData(clipData);
945 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700946
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700947 if (mState.action == ACTION_GET_CONTENT) {
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700948 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
949 } else {
950 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
951 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
952 | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
953 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -0700954
955 setResult(Activity.RESULT_OK, intent);
956 finish();
957 }
958
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700959 public static class State implements android.os.Parcelable {
Jeff Sharkey873daa32013-08-18 17:38:20 -0700960 public int action;
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700961 public String[] acceptMimes;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700962
963 /** Explicit user choice */
964 public int userMode = MODE_UNKNOWN;
965 /** Derived after loader */
966 public int derivedMode = MODE_LIST;
967
968 /** Explicit user choice */
969 public int userSortOrder = SORT_ORDER_UNKNOWN;
970 /** Derived after loader */
971 public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
972
Jeff Sharkeya5defe32013-08-05 17:56:48 -0700973 public boolean allowMultiple = false;
Jeff Sharkey2e694f82013-08-06 16:26:14 -0700974 public boolean showSize = false;
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700975 public boolean localOnly = false;
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700976 public boolean showAdvanced = false;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -0700977 public boolean stackTouched = false;
Jeff Sharkey8b997042013-09-19 15:25:56 -0700978 public boolean restored = false;
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700979
Jeff Sharkeyb3620442013-09-01 18:41:04 -0700980 /** Current user navigation stack; empty implies recents. */
981 public DocumentStack stack = new DocumentStack();
982 /** Currently active search, overriding any stack. */
983 public String currentSearch;
984
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -0700985 /** Instance state for every shown directory */
986 public HashMap<String, SparseArray<Parcelable>> dirState = Maps.newHashMap();
987
Jeff Sharkey873daa32013-08-18 17:38:20 -0700988 public static final int ACTION_OPEN = 1;
989 public static final int ACTION_CREATE = 2;
990 public static final int ACTION_GET_CONTENT = 3;
991 public static final int ACTION_MANAGE = 4;
992
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700993 public static final int MODE_UNKNOWN = 0;
994 public static final int MODE_LIST = 1;
995 public static final int MODE_GRID = 2;
Jeff Sharkey3c28b792013-07-01 17:22:02 -0700996
Jeff Sharkeya4d1f222013-09-07 14:45:03 -0700997 public static final int SORT_ORDER_UNKNOWN = 0;
998 public static final int SORT_ORDER_DISPLAY_NAME = 1;
999 public static final int SORT_ORDER_LAST_MODIFIED = 2;
1000 public static final int SORT_ORDER_SIZE = 3;
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001001
1002 @Override
1003 public int describeContents() {
1004 return 0;
1005 }
1006
1007 @Override
1008 public void writeToParcel(Parcel out, int flags) {
1009 out.writeInt(action);
Jeff Sharkeyd10f0492013-09-09 17:35:46 -07001010 out.writeInt(userMode);
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001011 out.writeStringArray(acceptMimes);
Jeff Sharkeyd10f0492013-09-09 17:35:46 -07001012 out.writeInt(userSortOrder);
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001013 out.writeInt(allowMultiple ? 1 : 0);
1014 out.writeInt(showSize ? 1 : 0);
1015 out.writeInt(localOnly ? 1 : 0);
Jeff Sharkey1c903cc2013-09-02 17:19:40 -07001016 out.writeInt(showAdvanced ? 1 : 0);
Jeff Sharkeyd10f0492013-09-09 17:35:46 -07001017 out.writeInt(stackTouched ? 1 : 0);
Jeff Sharkey8b997042013-09-19 15:25:56 -07001018 out.writeInt(restored ? 1 : 0);
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001019 DurableUtils.writeToParcel(out, stack);
1020 out.writeString(currentSearch);
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -07001021 out.writeMap(dirState);
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001022 }
1023
1024 public static final Creator<State> CREATOR = new Creator<State>() {
1025 @Override
1026 public State createFromParcel(Parcel in) {
1027 final State state = new State();
1028 state.action = in.readInt();
Jeff Sharkeyd10f0492013-09-09 17:35:46 -07001029 state.userMode = in.readInt();
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001030 state.acceptMimes = in.readStringArray();
Jeff Sharkeyd10f0492013-09-09 17:35:46 -07001031 state.userSortOrder = in.readInt();
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001032 state.allowMultiple = in.readInt() != 0;
1033 state.showSize = in.readInt() != 0;
1034 state.localOnly = in.readInt() != 0;
Jeff Sharkey1c903cc2013-09-02 17:19:40 -07001035 state.showAdvanced = in.readInt() != 0;
Jeff Sharkeyd10f0492013-09-09 17:35:46 -07001036 state.stackTouched = in.readInt() != 0;
Jeff Sharkey8b997042013-09-19 15:25:56 -07001037 state.restored = in.readInt() != 0;
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001038 DurableUtils.readFromParcel(in, state.stack);
1039 state.currentSearch = in.readString();
Jeff Sharkeyc8ae7a52013-09-18 16:26:49 -07001040 in.readMap(state.dirState, null);
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001041 return state;
1042 }
1043
1044 @Override
1045 public State[] newArray(int size) {
1046 return new State[size];
1047 }
1048 };
Jeff Sharkey3c28b792013-07-01 17:22:02 -07001049 }
1050
Jeff Sharkey5b535922013-08-02 15:55:26 -07001051 private void dumpStack() {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -07001052 Log.d(TAG, "Current stack: ");
1053 Log.d(TAG, " * " + mState.stack.root);
Jeff Sharkeyb3620442013-09-01 18:41:04 -07001054 for (DocumentInfo doc : mState.stack) {
Jeff Sharkeya4d1f222013-09-07 14:45:03 -07001055 Log.d(TAG, " +-- " + doc);
Jeff Sharkey5b535922013-08-02 15:55:26 -07001056 }
1057 }
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -07001058
1059 public static DocumentsActivity get(Fragment fragment) {
1060 return (DocumentsActivity) fragment.getActivity();
1061 }
Jeff Sharkeye22d02e2013-04-26 16:54:55 -07001062}