am c22acf6f: Merge "Instance state, fix sharing, Durable objects." into klp-dev

* commit 'c22acf6f1fc001ad0f1c23c5261f371d63e4106e':
  Instance state, fix sharing, Durable objects.
This commit is contained in:
Jeff Sharkey
2013-09-02 20:51:40 -07:00
committed by Android Git Automerger
12 changed files with 499 additions and 210 deletions

View File

@@ -11,10 +11,7 @@
<!-- TODO: allow rotation when state saving is in better shape --> <!-- TODO: allow rotation when state saving is in better shape -->
<activity <activity
android:name=".DocumentsActivity" android:name=".DocumentsActivity"
android:finishOnCloseSystemDialogs="true" android:theme="@android:style/Theme.Holo.Light">
android:excludeFromRecents="true"
android:theme="@android:style/Theme.Holo.Light"
android:screenOrientation="nosensor">
<intent-filter android:priority="100"> <intent-filter android:priority="100">
<action android:name="android.intent.action.OPEN_DOCUMENT" /> <action android:name="android.intent.action.OPEN_DOCUMENT" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
@@ -37,7 +34,7 @@
<intent-filter> <intent-filter>
<action android:name="android.provider.action.MANAGE_DOCUMENTS" /> <action android:name="android.provider.action.MANAGE_DOCUMENTS" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.doc/dir" /> <data android:mimeType="vnd.android.document/directory" />
</intent-filter> </intent-filter>
</activity> </activity>

View File

@@ -63,4 +63,6 @@
<string name="more">More</string> <string name="more">More</string>
<string name="loading">Loading\u2026</string> <string name="loading">Loading\u2026</string>
<string name="share_via">Share via</string>
</resources> </resources>

View File

@@ -17,9 +17,9 @@
package com.android.documentsui; package com.android.documentsui;
import static com.android.documentsui.DocumentsActivity.TAG; import static com.android.documentsui.DocumentsActivity.TAG;
import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_MANAGE; import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_GRID; import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_LIST; import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
import static com.android.documentsui.model.DocumentInfo.getCursorInt; import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import static com.android.documentsui.model.DocumentInfo.getCursorLong; import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString; import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -62,7 +62,7 @@ import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.android.documentsui.DocumentsActivity.DisplayState; import com.android.documentsui.DocumentsActivity.State;
import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentInfo;
import com.android.internal.util.Predicate; import com.android.internal.util.Predicate;
import com.google.android.collect.Lists; import com.google.android.collect.Lists;
@@ -168,7 +168,7 @@ public class DirectoryFragment extends Fragment {
mCallbacks = new LoaderCallbacks<DirectoryResult>() { mCallbacks = new LoaderCallbacks<DirectoryResult>() {
@Override @Override
public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) { public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
final DisplayState state = getDisplayState(DirectoryFragment.this); final State state = getDisplayState(DirectoryFragment.this);
Uri contentsUri; Uri contentsUri;
if (mType == TYPE_NORMAL) { if (mType == TYPE_NORMAL) {
@@ -196,7 +196,7 @@ public class DirectoryFragment extends Fragment {
} }
public void updateDisplayState() { public void updateDisplayState() {
final DisplayState state = getDisplayState(this); final State state = getDisplayState(this);
if (mLastSortOrder != state.sortOrder) { if (mLastSortOrder != state.sortOrder) {
getLoaderManager().restartLoader(mLoaderId, null, mCallbacks); getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
@@ -263,7 +263,7 @@ public class DirectoryFragment extends Fragment {
@Override @Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) { public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
final DisplayState state = getDisplayState(DirectoryFragment.this); final State state = getDisplayState(DirectoryFragment.this);
final MenuItem open = menu.findItem(R.id.menu_open); final MenuItem open = menu.findItem(R.id.menu_open);
final MenuItem share = menu.findItem(R.id.menu_share); final MenuItem share = menu.findItem(R.id.menu_share);
@@ -294,14 +294,17 @@ public class DirectoryFragment extends Fragment {
final int id = item.getItemId(); final int id = item.getItemId();
if (id == R.id.menu_open) { if (id == R.id.menu_open) {
DocumentsActivity.get(DirectoryFragment.this).onDocumentsPicked(docs); DocumentsActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
mode.finish();
return true; return true;
} else if (id == R.id.menu_share) { } else if (id == R.id.menu_share) {
onShareDocuments(docs); onShareDocuments(docs);
mode.finish();
return true; return true;
} else if (id == R.id.menu_delete) { } else if (id == R.id.menu_delete) {
onDeleteDocuments(docs); onDeleteDocuments(docs);
mode.finish();
return true; return true;
} else { } else {
@@ -332,26 +335,36 @@ public class DirectoryFragment extends Fragment {
}; };
private void onShareDocuments(List<DocumentInfo> docs) { private void onShareDocuments(List<DocumentInfo> docs) {
final ArrayList<Uri> uris = Lists.newArrayList(); Intent intent;
for (DocumentInfo doc : docs) { if (docs.size() == 1) {
uris.add(doc.uri); final DocumentInfo doc = docs.get(0);
}
final Intent intent;
if (uris.size() > 1) {
intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addCategory(Intent.CATEGORY_DEFAULT);
// TODO: find common mimetype
intent.setType("*/*");
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
} else {
intent = new Intent(Intent.ACTION_SEND); intent = new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addCategory(Intent.CATEGORY_DEFAULT); intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(uris.get(0)); intent.setType(doc.mimeType);
intent.putExtra(Intent.EXTRA_STREAM, doc.uri);
} else if (docs.size() > 1) {
intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addCategory(Intent.CATEGORY_DEFAULT);
final ArrayList<String> mimeTypes = Lists.newArrayList();
final ArrayList<Uri> uris = Lists.newArrayList();
for (DocumentInfo doc : docs) {
mimeTypes.add(doc.mimeType);
uris.add(doc.uri);
}
intent.setType(findCommonMimeType(mimeTypes));
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
} else {
return;
} }
intent = Intent.createChooser(intent, getActivity().getText(R.string.share_via));
startActivity(intent); startActivity(intent);
} }
@@ -383,7 +396,7 @@ public class DirectoryFragment extends Fragment {
} }
} }
private static DisplayState getDisplayState(Fragment fragment) { private static State getDisplayState(Fragment fragment) {
return ((DocumentsActivity) fragment.getActivity()).getDisplayState(); return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
} }
@@ -411,7 +424,7 @@ public class DirectoryFragment extends Fragment {
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
final Context context = parent.getContext(); final Context context = parent.getContext();
final DisplayState state = getDisplayState(DirectoryFragment.this); final State state = getDisplayState(DirectoryFragment.this);
final RootsCache roots = DocumentsApplication.getRootsCache(context); final RootsCache roots = DocumentsApplication.getRootsCache(context);
final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache( final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
@@ -586,4 +599,28 @@ public class DirectoryFragment extends Fragment {
return DateUtils.formatDateTime(context, when, flags); return DateUtils.formatDateTime(context, when, flags);
} }
private String findCommonMimeType(List<String> mimeTypes) {
String[] commonType = mimeTypes.get(0).split("/");
if (commonType.length != 2) {
return "*/*";
}
for (int i = 1; i < mimeTypes.size(); i++) {
String[] type = mimeTypes.get(i).split("/");
if (type.length != 2) continue;
if (!commonType[1].equals(type[1])) {
commonType[1] = "*";
}
if (!commonType[0].equals(type[0])) {
commonType[0] = "*";
commonType[1] = "*";
break;
}
}
return commonType[0] + "/" + commonType[1];
}
} }

View File

@@ -16,6 +16,10 @@
package com.android.documentsui; package com.android.documentsui;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_DISPLAY_NAME;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_SIZE;
import android.content.AsyncTaskLoader; import android.content.AsyncTaskLoader;
import android.content.ContentProviderClient; import android.content.ContentProviderClient;
import android.content.Context; import android.content.Context;
@@ -25,8 +29,6 @@ import android.os.CancellationSignal;
import android.os.OperationCanceledException; import android.os.OperationCanceledException;
import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Document;
import com.android.documentsui.DocumentsActivity.DisplayState;
import libcore.io.IoUtils; import libcore.io.IoUtils;
class DirectoryResult implements AutoCloseable { class DirectoryResult implements AutoCloseable {
@@ -149,11 +151,11 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
private String getQuerySortOrder() { private String getQuerySortOrder() {
switch (mSortOrder) { switch (mSortOrder) {
case DisplayState.SORT_ORDER_DISPLAY_NAME: case SORT_ORDER_DISPLAY_NAME:
return Document.COLUMN_DISPLAY_NAME + " ASC"; return Document.COLUMN_DISPLAY_NAME + " ASC";
case DisplayState.SORT_ORDER_LAST_MODIFIED: case SORT_ORDER_LAST_MODIFIED:
return Document.COLUMN_LAST_MODIFIED + " DESC"; return Document.COLUMN_LAST_MODIFIED + " DESC";
case DisplayState.SORT_ORDER_SIZE: case SORT_ORDER_SIZE:
return Document.COLUMN_SIZE + " DESC"; return Document.COLUMN_SIZE + " DESC";
default: default:
return null; return null;

View File

@@ -16,13 +16,13 @@
package com.android.documentsui; package com.android.documentsui;
import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_CREATE; import static com.android.documentsui.DocumentsActivity.State.ACTION_CREATE;
import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_GET_CONTENT; import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_MANAGE; import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_OPEN; import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN;
import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_GRID; import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_LIST; import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
import static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_LAST_MODIFIED; import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
import android.app.ActionBar; import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener; import android.app.ActionBar.OnNavigationListener;
@@ -41,6 +41,7 @@ import android.database.Cursor;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcel;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat; import android.support.v4.view.GravityCompat;
@@ -61,31 +62,27 @@ import android.widget.Toast;
import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo; import com.android.documentsui.model.RootInfo;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class DocumentsActivity extends Activity { public class DocumentsActivity extends Activity {
public static final String TAG = "Documents"; public static final String TAG = "Documents";
private int mAction;
private SearchView mSearchView; private SearchView mSearchView;
private View mRootsContainer; private View mRootsContainer;
private DrawerLayout mDrawerLayout; private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mDrawerToggle; private ActionBarDrawerToggle mDrawerToggle;
private final DisplayState mDisplayState = new DisplayState(); private static final String EXTRA_STATE = "state";
private RootsCache mRoots; private RootsCache mRoots;
private State mState;
/** Current user navigation stack; empty implies recents. */
private DocumentStack mStack = new DocumentStack();
/** Currently active search, overriding any stack. */
private String mCurrentSearch;
@Override @Override
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
@@ -93,59 +90,9 @@ public class DocumentsActivity extends Activity {
mRoots = DocumentsApplication.getRootsCache(this); mRoots = DocumentsApplication.getRootsCache(this);
final Intent intent = getIntent();
final String action = intent.getAction();
if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
mAction = ACTION_OPEN;
} else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
mAction = ACTION_CREATE;
} else if (Intent.ACTION_GET_CONTENT.equals(action)) {
mAction = ACTION_GET_CONTENT;
} else if (DocumentsContract.ACTION_MANAGE_DOCUMENTS.equals(action)) {
mAction = ACTION_MANAGE;
}
// TODO: unify action in single place
mDisplayState.action = mAction;
if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
mDisplayState.allowMultiple = intent.getBooleanExtra(
Intent.EXTRA_ALLOW_MULTIPLE, false);
}
if (mAction == ACTION_MANAGE) {
mDisplayState.acceptMimes = new String[] { "*/*" };
mDisplayState.allowMultiple = true;
} else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
mDisplayState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
} else {
mDisplayState.acceptMimes = new String[] { intent.getType() };
}
mDisplayState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
setResult(Activity.RESULT_CANCELED); setResult(Activity.RESULT_CANCELED);
setContentView(R.layout.activity); setContentView(R.layout.activity);
if (mAction == ACTION_CREATE) {
final String mimeType = getIntent().getType();
final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
SaveFragment.show(getFragmentManager(), mimeType, title);
}
if (mAction == ACTION_GET_CONTENT) {
final Intent moreApps = new Intent(getIntent());
moreApps.setComponent(null);
moreApps.setPackage(null);
RootsFragment.show(getFragmentManager(), moreApps);
} else if (mAction == ACTION_OPEN || mAction == ACTION_CREATE) {
RootsFragment.show(getFragmentManager(), null);
}
if (mAction == ACTION_MANAGE) {
mDisplayState.sortOrder = SORT_ORDER_LAST_MODIFIED;
}
mRootsContainer = findViewById(R.id.container_roots); mRootsContainer = findViewById(R.id.container_roots);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
@@ -156,9 +103,70 @@ public class DocumentsActivity extends Activity {
mDrawerLayout.setDrawerListener(mDrawerListener); mDrawerLayout.setDrawerListener(mDrawerListener);
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
if (mAction == ACTION_MANAGE) { if (icicle != null) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); mState = icicle.getParcelable(EXTRA_STATE);
} else {
buildDefaultState();
}
if (mState.action == ACTION_MANAGE) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
if (mState.action == ACTION_CREATE) {
final String mimeType = getIntent().getType();
final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
SaveFragment.show(getFragmentManager(), mimeType, title);
}
if (mState.action == ACTION_GET_CONTENT) {
final Intent moreApps = new Intent(getIntent());
moreApps.setComponent(null);
moreApps.setPackage(null);
RootsFragment.show(getFragmentManager(), moreApps);
} else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE) {
RootsFragment.show(getFragmentManager(), null);
}
onCurrentDirectoryChanged();
}
private void buildDefaultState() {
mState = new State();
final Intent intent = getIntent();
final String action = intent.getAction();
if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
mState.action = ACTION_OPEN;
} else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
mState.action = ACTION_CREATE;
} else if (Intent.ACTION_GET_CONTENT.equals(action)) {
mState.action = ACTION_GET_CONTENT;
} else if (DocumentsContract.ACTION_MANAGE_DOCUMENTS.equals(action)) {
mState.action = ACTION_MANAGE;
}
if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
mState.allowMultiple = intent.getBooleanExtra(
Intent.EXTRA_ALLOW_MULTIPLE, false);
}
if (mState.action == ACTION_MANAGE) {
mState.acceptMimes = new String[] { "*/*" };
mState.allowMultiple = true;
} else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
mState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
} else {
mState.acceptMimes = new String[] { intent.getType() };
}
mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
if (mState.action == ACTION_MANAGE) {
mState.sortOrder = SORT_ORDER_LAST_MODIFIED;
}
if (mState.action == ACTION_MANAGE) {
final Uri rootUri = intent.getData(); final Uri rootUri = intent.getData();
final RootInfo root = mRoots.findRoot(rootUri); final RootInfo root = mRoots.findRoot(rootUri);
if (root != null) { if (root != null) {
@@ -169,8 +177,6 @@ public class DocumentsActivity extends Activity {
} }
} else { } else {
mDrawerLayout.openDrawer(mRootsContainer);
// Restore last stack for calling package // Restore last stack for calling package
// TODO: move into async loader // TODO: move into async loader
final String packageName = getCallingPackage(); final String packageName = getCallingPackage();
@@ -178,17 +184,17 @@ public class DocumentsActivity extends Activity {
.query(RecentsProvider.buildResume(packageName), null, null, null, null); .query(RecentsProvider.buildResume(packageName), null, null, null, null);
try { try {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
final String raw = cursor.getString( final byte[] rawStack = cursor.getBlob(
cursor.getColumnIndex(RecentsProvider.COL_PATH)); cursor.getColumnIndex(RecentsProvider.COL_PATH));
mStack = DocumentStack.deserialize(getContentResolver(), raw); DurableUtils.readFromArray(rawStack, mState.stack);
} }
} catch (FileNotFoundException e) { } catch (IOException e) {
Log.w(TAG, "Failed to resume", e); Log.w(TAG, "Failed to resume", e);
} finally { } finally {
cursor.close(); cursor.close();
} }
onCurrentDirectoryChanged(); mDrawerLayout.openDrawer(mRootsContainer);
} }
} }
@@ -196,10 +202,10 @@ public class DocumentsActivity extends Activity {
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
if (mAction == ACTION_MANAGE) { if (mState.action == ACTION_MANAGE) {
mDisplayState.showSize = true; mState.showSize = true;
} else { } else {
mDisplayState.showSize = SettingsActivity.getDisplayFileSize(this); mState.showSize = SettingsActivity.getDisplayFileSize(this);
} }
} }
@@ -242,9 +248,9 @@ public class DocumentsActivity extends Activity {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setIcon(new ColorDrawable()); actionBar.setIcon(new ColorDrawable());
if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) { if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
actionBar.setTitle(R.string.title_open); actionBar.setTitle(R.string.title_open);
} else if (mAction == ACTION_CREATE) { } else if (mState.action == ACTION_CREATE) {
actionBar.setTitle(R.string.title_save); actionBar.setTitle(R.string.title_save);
} }
@@ -262,13 +268,13 @@ public class DocumentsActivity extends Activity {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
actionBar.setTitle(null); actionBar.setTitle(null);
actionBar.setListNavigationCallbacks(mSortAdapter, mSortListener); actionBar.setListNavigationCallbacks(mSortAdapter, mSortListener);
actionBar.setSelectedNavigationItem(mDisplayState.sortOrder); actionBar.setSelectedNavigationItem(mState.sortOrder);
} }
if (mStack.size() > 1) { if (mState.stack.size() > 1) {
actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true);
mDrawerToggle.setDrawerIndicatorEnabled(false); mDrawerToggle.setDrawerIndicatorEnabled(false);
} else if (mAction == ACTION_MANAGE) { } else if (mState.action == ACTION_MANAGE) {
actionBar.setDisplayHomeAsUpEnabled(false); actionBar.setDisplayHomeAsUpEnabled(false);
mDrawerToggle.setDrawerIndicatorEnabled(false); mDrawerToggle.setDrawerIndicatorEnabled(false);
} else { } else {
@@ -288,7 +294,7 @@ public class DocumentsActivity extends Activity {
mSearchView.setOnQueryTextListener(new OnQueryTextListener() { mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
@Override @Override
public boolean onQueryTextSubmit(String query) { public boolean onQueryTextSubmit(String query) {
mCurrentSearch = query; mState.currentSearch = query;
onCurrentDirectoryChanged(); onCurrentDirectoryChanged();
mSearchView.setIconified(true); mSearchView.setIconified(true);
return true; return true;
@@ -303,7 +309,7 @@ public class DocumentsActivity extends Activity {
mSearchView.setOnCloseListener(new OnCloseListener() { mSearchView.setOnCloseListener(new OnCloseListener() {
@Override @Override
public boolean onClose() { public boolean onClose() {
mCurrentSearch = null; mState.currentSearch = null;
onCurrentDirectoryChanged(); onCurrentDirectoryChanged();
return false; return false;
} }
@@ -325,11 +331,11 @@ public class DocumentsActivity extends Activity {
final MenuItem list = menu.findItem(R.id.menu_list); final MenuItem list = menu.findItem(R.id.menu_list);
final MenuItem settings = menu.findItem(R.id.menu_settings); final MenuItem settings = menu.findItem(R.id.menu_settings);
grid.setVisible(mDisplayState.mode != MODE_GRID); grid.setVisible(mState.mode != MODE_GRID);
list.setVisible(mDisplayState.mode != MODE_LIST); list.setVisible(mState.mode != MODE_LIST);
final boolean searchVisible; final boolean searchVisible;
if (mAction == ACTION_CREATE) { if (mState.action == ACTION_CREATE) {
createDir.setVisible(cwd != null && cwd.isCreateSupported()); createDir.setVisible(cwd != null && cwd.isCreateSupported());
searchVisible = false; searchVisible = false;
@@ -348,7 +354,7 @@ public class DocumentsActivity extends Activity {
// TODO: close any search in-progress when hiding // TODO: close any search in-progress when hiding
search.setVisible(searchVisible); search.setVisible(searchVisible);
settings.setVisible(mAction != ACTION_MANAGE); settings.setVisible(mState.action != ACTION_MANAGE);
return true; return true;
} }
@@ -370,13 +376,13 @@ public class DocumentsActivity extends Activity {
return false; return false;
} else if (id == R.id.menu_grid) { } else if (id == R.id.menu_grid) {
// TODO: persist explicit user mode for cwd // TODO: persist explicit user mode for cwd
mDisplayState.mode = MODE_GRID; mState.mode = MODE_GRID;
updateDisplayState(); updateDisplayState();
invalidateOptionsMenu(); invalidateOptionsMenu();
return true; return true;
} else if (id == R.id.menu_list) { } else if (id == R.id.menu_list) {
// TODO: persist explicit user mode for cwd // TODO: persist explicit user mode for cwd
mDisplayState.mode = MODE_LIST; mState.mode = MODE_LIST;
updateDisplayState(); updateDisplayState();
invalidateOptionsMenu(); invalidateOptionsMenu();
return true; return true;
@@ -390,9 +396,9 @@ public class DocumentsActivity extends Activity {
@Override @Override
public void onBackPressed() { public void onBackPressed() {
final int size = mStack.size(); final int size = mState.stack.size();
if (size > 1) { if (size > 1) {
mStack.pop(); mState.stack.pop();
onCurrentDirectoryChanged(); onCurrentDirectoryChanged();
} else if (size == 1 && !mDrawerLayout.isDrawerOpen(mRootsContainer)) { } else if (size == 1 && !mDrawerLayout.isDrawerOpen(mRootsContainer)) {
// TODO: open root drawer once we can capture back key // TODO: open root drawer once we can capture back key
@@ -402,11 +408,23 @@ public class DocumentsActivity extends Activity {
} }
} }
@Override
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
state.putParcelable(EXTRA_STATE, mState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
updateActionBar();
}
// TODO: support additional sort orders // TODO: support additional sort orders
private BaseAdapter mSortAdapter = new BaseAdapter() { private BaseAdapter mSortAdapter = new BaseAdapter() {
@Override @Override
public int getCount() { public int getCount() {
return mDisplayState.showSize ? 3 : 2; return mState.showSize ? 3 : 2;
} }
@Override @Override
@@ -438,8 +456,8 @@ public class DocumentsActivity extends Activity {
final TextView title = (TextView) convertView.findViewById(android.R.id.title); final TextView title = (TextView) convertView.findViewById(android.R.id.title);
final TextView summary = (TextView) convertView.findViewById(android.R.id.summary); final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
if (mStack.size() > 0) { if (mState.stack.size() > 0) {
title.setText(mStack.getTitle(mRoots)); title.setText(mState.stack.getTitle(mRoots));
} else { } else {
// No directory means recents // No directory means recents
title.setText(R.string.root_recent); title.setText(R.string.root_recent);
@@ -467,26 +485,26 @@ public class DocumentsActivity extends Activity {
private OnNavigationListener mSortListener = new OnNavigationListener() { private OnNavigationListener mSortListener = new OnNavigationListener() {
@Override @Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) { public boolean onNavigationItemSelected(int itemPosition, long itemId) {
mDisplayState.sortOrder = itemPosition; mState.sortOrder = itemPosition;
updateDisplayState(); updateDisplayState();
return true; return true;
} }
}; };
public RootInfo getCurrentRoot() { public RootInfo getCurrentRoot() {
if (mStack.size() > 0) { if (mState.stack.size() > 0) {
return mStack.getRoot(mRoots); return mState.stack.getRoot(mRoots);
} else { } else {
return mRoots.getRecentsRoot(); return mRoots.getRecentsRoot();
} }
} }
public DocumentInfo getCurrentDirectory() { public DocumentInfo getCurrentDirectory() {
return mStack.peek(); return mState.stack.peek();
} }
public DisplayState getDisplayState() { public State getDisplayState() {
return mDisplayState; return mState;
} }
private void onCurrentDirectoryChanged() { private void onCurrentDirectoryChanged() {
@@ -495,15 +513,15 @@ public class DocumentsActivity extends Activity {
if (cwd == null) { if (cwd == null) {
// No directory means recents // No directory means recents
if (mAction == ACTION_CREATE) { if (mState.action == ACTION_CREATE) {
RecentsCreateFragment.show(fm); RecentsCreateFragment.show(fm);
} else { } else {
DirectoryFragment.showRecentsOpen(fm); DirectoryFragment.showRecentsOpen(fm);
} }
} else { } else {
if (mCurrentSearch != null) { if (mState.currentSearch != null) {
// Ongoing search // Ongoing search
DirectoryFragment.showSearch(fm, cwd.uri, mCurrentSearch); DirectoryFragment.showSearch(fm, cwd.uri, mState.currentSearch);
} else { } else {
// Normal boring directory // Normal boring directory
DirectoryFragment.showNormal(fm, cwd.uri); DirectoryFragment.showNormal(fm, cwd.uri);
@@ -511,7 +529,7 @@ public class DocumentsActivity extends Activity {
} }
// Forget any replacement target // Forget any replacement target
if (mAction == ACTION_CREATE) { if (mState.action == ACTION_CREATE) {
final SaveFragment save = SaveFragment.get(fm); final SaveFragment save = SaveFragment.get(fm);
if (save != null) { if (save != null) {
save.setReplaceTarget(null); save.setReplaceTarget(null);
@@ -529,13 +547,13 @@ public class DocumentsActivity extends Activity {
} }
public void onStackPicked(DocumentStack stack) { public void onStackPicked(DocumentStack stack) {
mStack = stack; mState.stack = stack;
onCurrentDirectoryChanged(); onCurrentDirectoryChanged();
} }
public void onRootPicked(RootInfo root, boolean closeDrawer) { public void onRootPicked(RootInfo root, boolean closeDrawer) {
// Clear entire backstack and start in new root // Clear entire backstack and start in new root
mStack.clear(); mState.stack.clear();
if (!mRoots.isRecentsRoot(root)) { if (!mRoots.isRecentsRoot(root)) {
try { try {
@@ -566,19 +584,19 @@ public class DocumentsActivity extends Activity {
if (doc.isDirectory()) { if (doc.isDirectory()) {
// TODO: query display mode user preference for this dir // TODO: query display mode user preference for this dir
if (doc.isGridPreferred()) { if (doc.isGridPreferred()) {
mDisplayState.mode = MODE_GRID; mState.mode = MODE_GRID;
} else { } else {
mDisplayState.mode = MODE_LIST; mState.mode = MODE_LIST;
} }
mStack.push(doc); mState.stack.push(doc);
onCurrentDirectoryChanged(); onCurrentDirectoryChanged();
} else if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) { } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
// Explicit file picked, return // Explicit file picked, return
onFinished(doc.uri); onFinished(doc.uri);
} else if (mAction == ACTION_CREATE) { } else if (mState.action == ACTION_CREATE) {
// Replace selected file // Replace selected file
SaveFragment.get(fm).setReplaceTarget(doc); SaveFragment.get(fm).setReplaceTarget(doc);
} else if (mAction == ACTION_MANAGE) { } else if (mState.action == ACTION_MANAGE) {
// Open the document // Open the document
final Intent intent = new Intent(Intent.ACTION_VIEW); final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -592,7 +610,7 @@ public class DocumentsActivity extends Activity {
} }
public void onDocumentsPicked(List<DocumentInfo> docs) { public void onDocumentsPicked(List<DocumentInfo> docs) {
if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) { if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
final int size = docs.size(); final int size = docs.size();
final Uri[] uris = new Uri[size]; final Uri[] uris = new Uri[size];
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
@@ -629,14 +647,14 @@ public class DocumentsActivity extends Activity {
final ContentResolver resolver = getContentResolver(); final ContentResolver resolver = getContentResolver();
final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues();
final String rawStack = DocumentStack.serialize(mStack); final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
if (mAction == ACTION_CREATE) { if (mState.action == ACTION_CREATE) {
// Remember stack for last create // Remember stack for last create
values.clear(); values.clear();
values.put(RecentsProvider.COL_PATH, rawStack); values.put(RecentsProvider.COL_PATH, rawStack);
resolver.insert(RecentsProvider.buildRecentCreate(), values); resolver.insert(RecentsProvider.buildRecentCreate(), values);
} else if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) { } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
// Remember opened items // Remember opened items
for (Uri uri : uris) { for (Uri uri : uris) {
values.clear(); values.clear();
@@ -656,14 +674,14 @@ public class DocumentsActivity extends Activity {
intent.setData(uris[0]); intent.setData(uris[0]);
} else if (uris.length > 1) { } else if (uris.length > 1) {
final ClipData clipData = new ClipData( final ClipData clipData = new ClipData(
null, mDisplayState.acceptMimes, new ClipData.Item(uris[0])); null, mState.acceptMimes, new ClipData.Item(uris[0]));
for (int i = 1; i < uris.length; i++) { for (int i = 1; i < uris.length; i++) {
clipData.addItem(new ClipData.Item(uris[i])); clipData.addItem(new ClipData.Item(uris[i]));
} }
intent.setClipData(clipData); intent.setClipData(clipData);
} }
if (mAction == ACTION_GET_CONTENT) { if (mState.action == ACTION_GET_CONTENT) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else { } else {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -675,7 +693,7 @@ public class DocumentsActivity extends Activity {
finish(); finish();
} }
public static class DisplayState { public static class State implements android.os.Parcelable {
public int action; public int action;
public int mode = MODE_LIST; public int mode = MODE_LIST;
public String[] acceptMimes; public String[] acceptMimes;
@@ -684,6 +702,11 @@ public class DocumentsActivity extends Activity {
public boolean showSize = false; public boolean showSize = false;
public boolean localOnly = false; public boolean localOnly = false;
/** Current user navigation stack; empty implies recents. */
public DocumentStack stack = new DocumentStack();
/** Currently active search, overriding any stack. */
public String currentSearch;
public static final int ACTION_OPEN = 1; public static final int ACTION_OPEN = 1;
public static final int ACTION_CREATE = 2; public static final int ACTION_CREATE = 2;
public static final int ACTION_GET_CONTENT = 3; public static final int ACTION_GET_CONTENT = 3;
@@ -695,11 +718,51 @@ public class DocumentsActivity extends Activity {
public static final int SORT_ORDER_DISPLAY_NAME = 0; public static final int SORT_ORDER_DISPLAY_NAME = 0;
public static final int SORT_ORDER_LAST_MODIFIED = 1; public static final int SORT_ORDER_LAST_MODIFIED = 1;
public static final int SORT_ORDER_SIZE = 2; public static final int SORT_ORDER_SIZE = 2;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(action);
out.writeInt(mode);
out.writeStringArray(acceptMimes);
out.writeInt(sortOrder);
out.writeInt(allowMultiple ? 1 : 0);
out.writeInt(showSize ? 1 : 0);
out.writeInt(localOnly ? 1 : 0);
DurableUtils.writeToParcel(out, stack);
out.writeString(currentSearch);
}
public static final Creator<State> CREATOR = new Creator<State>() {
@Override
public State createFromParcel(Parcel in) {
final State state = new State();
state.action = in.readInt();
state.mode = in.readInt();
state.acceptMimes = in.readStringArray();
state.sortOrder = in.readInt();
state.allowMultiple = in.readInt() != 0;
state.showSize = in.readInt() != 0;
state.localOnly = in.readInt() != 0;
DurableUtils.readFromParcel(in, state.stack);
state.currentSearch = in.readString();
return state;
}
@Override
public State[] newArray(int size) {
return new State[size];
}
};
} }
private void dumpStack() { private void dumpStack() {
Log.d(TAG, "Current stack:"); Log.d(TAG, "Current stack:");
for (DocumentInfo doc : mStack) { for (DocumentInfo doc : mState.stack) {
Log.d(TAG, "--> " + doc); Log.d(TAG, "--> " + doc);
} }
} }

View File

@@ -47,7 +47,9 @@ import com.google.android.collect.Lists;
import libcore.io.IoUtils; import libcore.io.IoUtils;
import java.io.FileNotFoundException; import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -138,12 +140,13 @@ public class RecentsCreateFragment extends Fragment {
uri, null, null, null, RecentsProvider.COL_TIMESTAMP + " DESC", signal); uri, null, null, null, RecentsProvider.COL_TIMESTAMP + " DESC", signal);
try { try {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
final String rawStack = cursor.getString( final byte[] raw = cursor.getBlob(
cursor.getColumnIndex(RecentsProvider.COL_PATH)); cursor.getColumnIndex(RecentsProvider.COL_PATH));
try { try {
final DocumentStack stack = DocumentStack.deserialize(resolver, rawStack); final DocumentStack stack = new DocumentStack();
stack.read(new DataInputStream(new ByteArrayInputStream(raw)));
result.add(stack); result.add(stack);
} catch (FileNotFoundException e) { } catch (IOException e) {
Log.w(TAG, "Failed to resolve stack: " + e); Log.w(TAG, "Failed to resolve stack: " + e);
} }
} }

View File

@@ -50,6 +50,8 @@ public class RootsCache {
// TODO: cache roots in local provider to avoid spinning up backends // TODO: cache roots in local provider to avoid spinning up backends
// TODO: root updates should trigger UI refresh // TODO: root updates should trigger UI refresh
private static final boolean RECENTS_ENABLED = false;
private final Context mContext; private final Context mContext;
public List<RootInfo> mRoots = Lists.newArrayList(); public List<RootInfo> mRoots = Lists.newArrayList();
@@ -68,7 +70,7 @@ public class RootsCache {
public void update() { public void update() {
mRoots.clear(); mRoots.clear();
{ if (RECENTS_ENABLED) {
// Create special root for recents // Create special root for recents
final RootInfo root = new RootInfo(); final RootInfo root = new RootInfo();
root.rootType = Root.ROOT_TYPE_SHORTCUT; root.rootType = Root.ROOT_TYPE_SHORTCUT;

View File

@@ -16,12 +16,14 @@
package com.android.documentsui; package com.android.documentsui;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_DISPLAY_NAME;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_SIZE;
import android.database.AbstractCursor; import android.database.AbstractCursor;
import android.database.Cursor; import android.database.Cursor;
import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Document;
import com.android.documentsui.DocumentsActivity.DisplayState;
/** /**
* Cursor wrapper that presents a sorted view of the underlying cursor. Handles * Cursor wrapper that presents a sorted view of the underlying cursor. Handles
* common {@link Document} sorting modes, such as ordering directories first. * common {@link Document} sorting modes, such as ordering directories first.
@@ -39,12 +41,12 @@ public class SortingCursorWrapper extends AbstractCursor {
final int count = cursor.getCount(); final int count = cursor.getCount();
mPosition = new int[count]; mPosition = new int[count];
switch (sortOrder) { switch (sortOrder) {
case DisplayState.SORT_ORDER_DISPLAY_NAME: case SORT_ORDER_DISPLAY_NAME:
mValueString = new String[count]; mValueString = new String[count];
mValueLong = null; mValueLong = null;
break; break;
case DisplayState.SORT_ORDER_LAST_MODIFIED: case SORT_ORDER_LAST_MODIFIED:
case DisplayState.SORT_ORDER_SIZE: case SORT_ORDER_SIZE:
mValueString = null; mValueString = null;
mValueLong = new long[count]; mValueLong = new long[count];
break; break;
@@ -63,7 +65,7 @@ public class SortingCursorWrapper extends AbstractCursor {
mPosition[i] = i; mPosition[i] = i;
switch (sortOrder) { switch (sortOrder) {
case DisplayState.SORT_ORDER_DISPLAY_NAME: case SORT_ORDER_DISPLAY_NAME:
final String mimeType = cursor.getString(mimeTypeIndex); final String mimeType = cursor.getString(mimeTypeIndex);
final String displayName = cursor.getString(displayNameIndex); final String displayName = cursor.getString(displayNameIndex);
if (Document.MIME_TYPE_DIR.equals(mimeType)) { if (Document.MIME_TYPE_DIR.equals(mimeType)) {
@@ -72,24 +74,24 @@ public class SortingCursorWrapper extends AbstractCursor {
mValueString[i] = displayName; mValueString[i] = displayName;
} }
break; break;
case DisplayState.SORT_ORDER_LAST_MODIFIED: case SORT_ORDER_LAST_MODIFIED:
mValueLong[i] = cursor.getLong(lastModifiedIndex); mValueLong[i] = cursor.getLong(lastModifiedIndex);
break; break;
case DisplayState.SORT_ORDER_SIZE: case SORT_ORDER_SIZE:
mValueLong[i] = cursor.getLong(sizeIndex); mValueLong[i] = cursor.getLong(sizeIndex);
break; break;
} }
} }
switch (sortOrder) { switch (sortOrder) {
case DisplayState.SORT_ORDER_DISPLAY_NAME: case SORT_ORDER_DISPLAY_NAME:
synchronized (SortingCursorWrapper.class) { synchronized (SortingCursorWrapper.class) {
binarySort(mPosition, mValueString); binarySort(mPosition, mValueString);
} }
break; break;
case DisplayState.SORT_ORDER_LAST_MODIFIED: case SORT_ORDER_LAST_MODIFIED:
case DisplayState.SORT_ORDER_SIZE: case SORT_ORDER_SIZE:
binarySort(mPosition, mValueLong); binarySort(mPosition, mValueLong);
break; break;
} }

View File

@@ -30,13 +30,19 @@ import com.android.documentsui.RecentsProvider;
import libcore.io.IoUtils; import libcore.io.IoUtils;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.ProtocolException;
import java.util.Comparator; import java.util.Comparator;
/** /**
* Representation of a {@link Document}. * Representation of a {@link Document}.
*/ */
public class DocumentInfo { public class DocumentInfo implements Durable {
private static final int VERSION_INIT = 1;
public Uri uri; public Uri uri;
public String mimeType; public String mimeType;
public String displayName; public String displayName;
@@ -46,6 +52,55 @@ public class DocumentInfo {
public long size; public long size;
public int icon; public int icon;
public DocumentInfo() {
reset();
}
@Override
public void reset() {
uri = null;
mimeType = null;
displayName = null;
lastModified = -1;
flags = 0;
summary = null;
size = -1;
icon = 0;
}
@Override
public void read(DataInputStream in) throws IOException {
final int version = in.readInt();
switch (version) {
case VERSION_INIT:
final String rawUri = DurableUtils.readNullableString(in);
uri = rawUri != null ? Uri.parse(rawUri) : null;
mimeType = DurableUtils.readNullableString(in);
displayName = DurableUtils.readNullableString(in);
lastModified = in.readLong();
flags = in.readInt();
summary = DurableUtils.readNullableString(in);
size = in.readLong();
icon = in.readInt();
break;
default:
throw new ProtocolException("Unknown version " + version);
}
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(VERSION_INIT);
DurableUtils.writeNullableString(out, uri.toString());
DurableUtils.writeNullableString(out, mimeType);
DurableUtils.writeNullableString(out, displayName);
out.writeLong(lastModified);
out.writeInt(flags);
DurableUtils.writeNullableString(out, summary);
out.writeLong(size);
out.writeInt(icon);
}
public static DocumentInfo fromDirectoryCursor(Uri parent, Cursor cursor) { public static DocumentInfo fromDirectoryCursor(Uri parent, Cursor cursor) {
final DocumentInfo doc = new DocumentInfo(); final DocumentInfo doc = new DocumentInfo();
final String authority = parent.getAuthority(); final String authority = parent.getAuthority();

View File

@@ -16,54 +16,20 @@
package com.android.documentsui.model; package com.android.documentsui.model;
import static com.android.documentsui.DocumentsActivity.TAG;
import static com.android.documentsui.model.DocumentInfo.asFileNotFoundException;
import android.content.ContentResolver;
import android.net.Uri;
import android.util.Log;
import com.android.documentsui.RootsCache; import com.android.documentsui.RootsCache;
import org.json.JSONArray; import java.io.DataInputStream;
import org.json.JSONException; import java.io.DataOutputStream;
import java.io.IOException;
import java.io.FileNotFoundException; import java.net.ProtocolException;
import java.util.LinkedList; import java.util.LinkedList;
/** /**
* Representation of a stack of {@link DocumentInfo}, usually the result of a * Representation of a stack of {@link DocumentInfo}, usually the result of a
* user-driven traversal. * user-driven traversal.
*/ */
public class DocumentStack extends LinkedList<DocumentInfo> { public class DocumentStack extends LinkedList<DocumentInfo> implements Durable {
private static final int VERSION_INIT = 1;
public static String serialize(DocumentStack stack) {
final JSONArray json = new JSONArray();
for (int i = 0; i < stack.size(); i++) {
json.put(stack.get(i).uri);
}
return json.toString();
}
public static DocumentStack deserialize(ContentResolver resolver, String raw)
throws FileNotFoundException {
Log.d(TAG, "deserialize: " + raw);
final DocumentStack stack = new DocumentStack();
try {
final JSONArray json = new JSONArray(raw);
for (int i = 0; i < json.length(); i++) {
final Uri uri = Uri.parse(json.getString(i));
final DocumentInfo doc = DocumentInfo.fromUri(resolver, uri);
stack.add(doc);
}
} catch (JSONException e) {
throw asFileNotFoundException(e);
}
// TODO: handle roots that have gone missing
return stack;
}
public RootInfo getRoot(RootsCache roots) { public RootInfo getRoot(RootsCache roots) {
return roots.findRoot(getLast().uri); return roots.findRoot(getLast().uri);
@@ -78,4 +44,37 @@ public class DocumentStack extends LinkedList<DocumentInfo> {
return null; return null;
} }
} }
@Override
public void reset() {
clear();
}
@Override
public void read(DataInputStream in) throws IOException {
final int version = in.readInt();
switch (version) {
case VERSION_INIT:
final int size = in.readInt();
for (int i = 0; i < size; i++) {
final DocumentInfo doc = new DocumentInfo();
doc.read(in);
add(doc);
}
break;
default:
throw new ProtocolException("Unknown version " + version);
}
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(VERSION_INIT);
final int size = size();
out.writeInt(size);
for (int i = 0; i < size; i++) {
final DocumentInfo doc = get(i);
doc.write(out);
}
}
} }

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.documentsui.model;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public interface Durable {
public void reset();
public void read(DataInputStream in) throws IOException;
public void write(DataOutputStream out) throws IOException;
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.documentsui.model;
import static com.android.documentsui.DocumentsActivity.TAG;
import android.os.BadParcelableException;
import android.os.Parcel;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class DurableUtils {
public static <D extends Durable> byte[] writeToArray(D d) throws IOException {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
d.write(new DataOutputStream(out));
return out.toByteArray();
}
public static <D extends Durable> D readFromArray(byte[] data, D d) throws IOException {
final ByteArrayInputStream in = new ByteArrayInputStream(data);
d.reset();
try {
d.read(new DataInputStream(in));
} catch (IOException e) {
d.reset();
throw e;
}
return d;
}
public static <D extends Durable> byte[] writeToArrayOrNull(D d) {
try {
return writeToArray(d);
} catch (IOException e) {
Log.w(TAG, "Failed to write", e);
return null;
}
}
public static <D extends Durable> D readFromArrayOrNull(byte[] data, D d) {
try {
return readFromArray(data, d);
} catch (IOException e) {
Log.w(TAG, "Failed to read", e);
return null;
}
}
public static <D extends Durable> void writeToParcel(Parcel parcel, D d) {
try {
parcel.writeByteArray(writeToArray(d));
} catch (IOException e) {
throw new BadParcelableException(e);
}
}
public static <D extends Durable> D readFromParcel(Parcel parcel, D d) {
try {
return readFromArray(parcel.createByteArray(), d);
} catch (IOException e) {
throw new BadParcelableException(e);
}
}
public static void writeNullableString(DataOutputStream out, String value) throws IOException {
if (value != null) {
out.write(1);
out.writeUTF(value);
} else {
out.write(0);
}
}
public static String readNullableString(DataInputStream in) throws IOException {
if (in.read() != 0) {
return in.readUTF();
} else {
return null;
}
}
}