summaryrefslogtreecommitdiffstats
path: root/packages/DocumentsUI/src/com/android/documentsui
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-09-01 18:41:04 -0700
committerJeff Sharkey <jsharkey@android.com>2013-09-01 18:59:38 -0700
commitb51331116eb2ebbc41aaf69142916f9af6dffdd5 (patch)
treeb64f0533ab030ae0c1349fe166b093f716794ace /packages/DocumentsUI/src/com/android/documentsui
parent5dfb345df7cb17b3a7e534a80a270b4afe7934da (diff)
downloadframeworks_base-b51331116eb2ebbc41aaf69142916f9af6dffdd5.zip
frameworks_base-b51331116eb2ebbc41aaf69142916f9af6dffdd5.tar.gz
frameworks_base-b51331116eb2ebbc41aaf69142916f9af6dffdd5.tar.bz2
Instance state, fix sharing, Durable objects.
Remember instance state across configuration changes, and enable rotation. This remembers current modes and in-progress traversals. Always finish action modes after launching an action. Fix sharing by always putting Uris in extras, and always wrap in a chooser. Find common MIME types when sharing multiple documents. Fix downloads launching by following directory MIME type change. Introduce "Durable" which is like Parcelable, but can be used for both byte[] storage and Parcel transport. Make both DocumentInfo and DocumentStack durable. Disable recents until new behavior is implemented. Bug: 10460236, 10446265, 10533674, 10456344, 10456702 Change-Id: I4eaf2b0b4cde611c69a1e7b5f1586f6b02019b27
Diffstat (limited to 'packages/DocumentsUI/src/com/android/documentsui')
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java79
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java12
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java281
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java11
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsCache.java4
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java24
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java57
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java79
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/Durable.java27
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java100
10 files changed, 482 insertions, 192 deletions
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 79f846a..549e196 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -17,9 +17,9 @@
package com.android.documentsui;
import static com.android.documentsui.DocumentsActivity.TAG;
-import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_MANAGE;
-import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_GRID;
-import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_LIST;
+import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
+import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
+import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -62,7 +62,7 @@ import android.widget.ListView;
import android.widget.TextView;
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.internal.util.Predicate;
import com.google.android.collect.Lists;
@@ -168,7 +168,7 @@ public class DirectoryFragment extends Fragment {
mCallbacks = new LoaderCallbacks<DirectoryResult>() {
@Override
public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
- final DisplayState state = getDisplayState(DirectoryFragment.this);
+ final State state = getDisplayState(DirectoryFragment.this);
Uri contentsUri;
if (mType == TYPE_NORMAL) {
@@ -196,7 +196,7 @@ public class DirectoryFragment extends Fragment {
}
public void updateDisplayState() {
- final DisplayState state = getDisplayState(this);
+ final State state = getDisplayState(this);
if (mLastSortOrder != state.sortOrder) {
getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
@@ -263,7 +263,7 @@ public class DirectoryFragment extends Fragment {
@Override
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 share = menu.findItem(R.id.menu_share);
@@ -294,14 +294,17 @@ public class DirectoryFragment extends Fragment {
final int id = item.getItemId();
if (id == R.id.menu_open) {
DocumentsActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
+ mode.finish();
return true;
} else if (id == R.id.menu_share) {
onShareDocuments(docs);
+ mode.finish();
return true;
} else if (id == R.id.menu_delete) {
onDeleteDocuments(docs);
+ mode.finish();
return true;
} else {
@@ -332,26 +335,36 @@ public class DirectoryFragment extends Fragment {
};
private void onShareDocuments(List<DocumentInfo> docs) {
- final ArrayList<Uri> uris = Lists.newArrayList();
- for (DocumentInfo doc : docs) {
- uris.add(doc.uri);
- }
+ Intent intent;
+ if (docs.size() == 1) {
+ final DocumentInfo doc = docs.get(0);
+
+ intent = new Intent(Intent.ACTION_SEND);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setType(doc.mimeType);
+ intent.putExtra(Intent.EXTRA_STREAM, doc.uri);
- final Intent intent;
- if (uris.size() > 1) {
+ } 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);
- // TODO: find common mimetype
- intent.setType("*/*");
+
+ 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 {
- intent = new Intent(Intent.ACTION_SEND);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.setData(uris.get(0));
+ return;
}
+ intent = Intent.createChooser(intent, getActivity().getText(R.string.share_via));
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();
}
@@ -411,7 +424,7 @@ public class DirectoryFragment extends Fragment {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final Context context = parent.getContext();
- final DisplayState state = getDisplayState(DirectoryFragment.this);
+ final State state = getDisplayState(DirectoryFragment.this);
final RootsCache roots = DocumentsApplication.getRootsCache(context);
final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
@@ -586,4 +599,28 @@ public class DirectoryFragment extends Fragment {
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];
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index b2be11b..fa674d5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -16,6 +16,10 @@
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.ContentProviderClient;
import android.content.Context;
@@ -25,8 +29,6 @@ import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.provider.DocumentsContract.Document;
-import com.android.documentsui.DocumentsActivity.DisplayState;
-
import libcore.io.IoUtils;
class DirectoryResult implements AutoCloseable {
@@ -149,11 +151,11 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
private String getQuerySortOrder() {
switch (mSortOrder) {
- case DisplayState.SORT_ORDER_DISPLAY_NAME:
+ case SORT_ORDER_DISPLAY_NAME:
return Document.COLUMN_DISPLAY_NAME + " ASC";
- case DisplayState.SORT_ORDER_LAST_MODIFIED:
+ case SORT_ORDER_LAST_MODIFIED:
return Document.COLUMN_LAST_MODIFIED + " DESC";
- case DisplayState.SORT_ORDER_SIZE:
+ case SORT_ORDER_SIZE:
return Document.COLUMN_SIZE + " DESC";
default:
return null;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 912d6dc..da790cc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -16,13 +16,13 @@
package com.android.documentsui;
-import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_CREATE;
-import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_GET_CONTENT;
-import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_MANAGE;
-import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_OPEN;
-import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_GRID;
-import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_LIST;
-import static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.DocumentsActivity.State.ACTION_CREATE;
+import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
+import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
+import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN;
+import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
+import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
+import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener;
@@ -41,6 +41,7 @@ import android.database.Cursor;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Parcel;
import android.provider.DocumentsContract;
import android.support.v4.app.ActionBarDrawerToggle;
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.DocumentStack;
+import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class DocumentsActivity extends Activity {
public static final String TAG = "Documents";
- private int mAction;
-
private SearchView mSearchView;
private View mRootsContainer;
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mDrawerToggle;
- private final DisplayState mDisplayState = new DisplayState();
+ private static final String EXTRA_STATE = "state";
private RootsCache mRoots;
-
- /** Current user navigation stack; empty implies recents. */
- private DocumentStack mStack = new DocumentStack();
- /** Currently active search, overriding any stack. */
- private String mCurrentSearch;
+ private State mState;
@Override
public void onCreate(Bundle icicle) {
@@ -93,72 +90,83 @@ public class DocumentsActivity extends Activity {
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;
- }
+ setResult(Activity.RESULT_CANCELED);
+ setContentView(R.layout.activity);
- // TODO: unify action in single place
- mDisplayState.action = mAction;
+ mRootsContainer = findViewById(R.id.container_roots);
- if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
- mDisplayState.allowMultiple = intent.getBooleanExtra(
- Intent.EXTRA_ALLOW_MULTIPLE, false);
- }
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
- 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);
+ mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
+ R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
+
+ mDrawerLayout.setDrawerListener(mDrawerListener);
+ mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
+
+ if (icicle != null) {
+ mState = icicle.getParcelable(EXTRA_STATE);
} else {
- mDisplayState.acceptMimes = new String[] { intent.getType() };
+ buildDefaultState();
}
- mDisplayState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
-
- setResult(Activity.RESULT_CANCELED);
- setContentView(R.layout.activity);
+ if (mState.action == ACTION_MANAGE) {
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+ }
- if (mAction == ACTION_CREATE) {
+ if (mState.action == 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) {
+ if (mState.action == 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) {
+ } else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE) {
RootsFragment.show(getFragmentManager(), null);
}
- if (mAction == ACTION_MANAGE) {
- mDisplayState.sortOrder = SORT_ORDER_LAST_MODIFIED;
- }
+ onCurrentDirectoryChanged();
+ }
- mRootsContainer = findViewById(R.id.container_roots);
+ private void buildDefaultState() {
+ mState = new State();
- mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ 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;
+ }
- mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
- R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
+ if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
+ mState.allowMultiple = intent.getBooleanExtra(
+ Intent.EXTRA_ALLOW_MULTIPLE, false);
+ }
- mDrawerLayout.setDrawerListener(mDrawerListener);
- mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
+ 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() };
+ }
- if (mAction == ACTION_MANAGE) {
- mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+ 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 RootInfo root = mRoots.findRoot(rootUri);
if (root != null) {
@@ -169,8 +177,6 @@ public class DocumentsActivity extends Activity {
}
} else {
- mDrawerLayout.openDrawer(mRootsContainer);
-
// Restore last stack for calling package
// TODO: move into async loader
final String packageName = getCallingPackage();
@@ -178,17 +184,17 @@ public class DocumentsActivity extends Activity {
.query(RecentsProvider.buildResume(packageName), null, null, null, null);
try {
if (cursor.moveToFirst()) {
- final String raw = cursor.getString(
+ final byte[] rawStack = cursor.getBlob(
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);
} finally {
cursor.close();
}
- onCurrentDirectoryChanged();
+ mDrawerLayout.openDrawer(mRootsContainer);
}
}
@@ -196,10 +202,10 @@ public class DocumentsActivity extends Activity {
public void onStart() {
super.onStart();
- if (mAction == ACTION_MANAGE) {
- mDisplayState.showSize = true;
+ if (mState.action == ACTION_MANAGE) {
+ mState.showSize = true;
} 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.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);
- } else if (mAction == ACTION_CREATE) {
+ } else if (mState.action == ACTION_CREATE) {
actionBar.setTitle(R.string.title_save);
}
@@ -262,13 +268,13 @@ public class DocumentsActivity extends Activity {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
actionBar.setTitle(null);
actionBar.setListNavigationCallbacks(mSortAdapter, mSortListener);
- actionBar.setSelectedNavigationItem(mDisplayState.sortOrder);
+ actionBar.setSelectedNavigationItem(mState.sortOrder);
}
- if (mStack.size() > 1) {
+ if (mState.stack.size() > 1) {
actionBar.setDisplayHomeAsUpEnabled(true);
mDrawerToggle.setDrawerIndicatorEnabled(false);
- } else if (mAction == ACTION_MANAGE) {
+ } else if (mState.action == ACTION_MANAGE) {
actionBar.setDisplayHomeAsUpEnabled(false);
mDrawerToggle.setDrawerIndicatorEnabled(false);
} else {
@@ -288,7 +294,7 @@ public class DocumentsActivity extends Activity {
mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
- mCurrentSearch = query;
+ mState.currentSearch = query;
onCurrentDirectoryChanged();
mSearchView.setIconified(true);
return true;
@@ -303,7 +309,7 @@ public class DocumentsActivity extends Activity {
mSearchView.setOnCloseListener(new OnCloseListener() {
@Override
public boolean onClose() {
- mCurrentSearch = null;
+ mState.currentSearch = null;
onCurrentDirectoryChanged();
return false;
}
@@ -325,11 +331,11 @@ public class DocumentsActivity extends Activity {
final MenuItem list = menu.findItem(R.id.menu_list);
final MenuItem settings = menu.findItem(R.id.menu_settings);
- grid.setVisible(mDisplayState.mode != MODE_GRID);
- list.setVisible(mDisplayState.mode != MODE_LIST);
+ grid.setVisible(mState.mode != MODE_GRID);
+ list.setVisible(mState.mode != MODE_LIST);
final boolean searchVisible;
- if (mAction == ACTION_CREATE) {
+ if (mState.action == ACTION_CREATE) {
createDir.setVisible(cwd != null && cwd.isCreateSupported());
searchVisible = false;
@@ -348,7 +354,7 @@ public class DocumentsActivity extends Activity {
// TODO: close any search in-progress when hiding
search.setVisible(searchVisible);
- settings.setVisible(mAction != ACTION_MANAGE);
+ settings.setVisible(mState.action != ACTION_MANAGE);
return true;
}
@@ -370,13 +376,13 @@ public class DocumentsActivity extends Activity {
return false;
} else if (id == R.id.menu_grid) {
// TODO: persist explicit user mode for cwd
- mDisplayState.mode = MODE_GRID;
+ mState.mode = MODE_GRID;
updateDisplayState();
invalidateOptionsMenu();
return true;
} else if (id == R.id.menu_list) {
// TODO: persist explicit user mode for cwd
- mDisplayState.mode = MODE_LIST;
+ mState.mode = MODE_LIST;
updateDisplayState();
invalidateOptionsMenu();
return true;
@@ -390,9 +396,9 @@ public class DocumentsActivity extends Activity {
@Override
public void onBackPressed() {
- final int size = mStack.size();
+ final int size = mState.stack.size();
if (size > 1) {
- mStack.pop();
+ mState.stack.pop();
onCurrentDirectoryChanged();
} else if (size == 1 && !mDrawerLayout.isDrawerOpen(mRootsContainer)) {
// 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
private BaseAdapter mSortAdapter = new BaseAdapter() {
@Override
public int getCount() {
- return mDisplayState.showSize ? 3 : 2;
+ return mState.showSize ? 3 : 2;
}
@Override
@@ -438,8 +456,8 @@ public class DocumentsActivity extends Activity {
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
- if (mStack.size() > 0) {
- title.setText(mStack.getTitle(mRoots));
+ if (mState.stack.size() > 0) {
+ title.setText(mState.stack.getTitle(mRoots));
} else {
// No directory means recents
title.setText(R.string.root_recent);
@@ -467,26 +485,26 @@ public class DocumentsActivity extends Activity {
private OnNavigationListener mSortListener = new OnNavigationListener() {
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
- mDisplayState.sortOrder = itemPosition;
+ mState.sortOrder = itemPosition;
updateDisplayState();
return true;
}
};
public RootInfo getCurrentRoot() {
- if (mStack.size() > 0) {
- return mStack.getRoot(mRoots);
+ if (mState.stack.size() > 0) {
+ return mState.stack.getRoot(mRoots);
} else {
return mRoots.getRecentsRoot();
}
}
public DocumentInfo getCurrentDirectory() {
- return mStack.peek();
+ return mState.stack.peek();
}
- public DisplayState getDisplayState() {
- return mDisplayState;
+ public State getDisplayState() {
+ return mState;
}
private void onCurrentDirectoryChanged() {
@@ -495,15 +513,15 @@ public class DocumentsActivity extends Activity {
if (cwd == null) {
// No directory means recents
- if (mAction == ACTION_CREATE) {
+ if (mState.action == ACTION_CREATE) {
RecentsCreateFragment.show(fm);
} else {
DirectoryFragment.showRecentsOpen(fm);
}
} else {
- if (mCurrentSearch != null) {
+ if (mState.currentSearch != null) {
// Ongoing search
- DirectoryFragment.showSearch(fm, cwd.uri, mCurrentSearch);
+ DirectoryFragment.showSearch(fm, cwd.uri, mState.currentSearch);
} else {
// Normal boring directory
DirectoryFragment.showNormal(fm, cwd.uri);
@@ -511,7 +529,7 @@ public class DocumentsActivity extends Activity {
}
// Forget any replacement target
- if (mAction == ACTION_CREATE) {
+ if (mState.action == ACTION_CREATE) {
final SaveFragment save = SaveFragment.get(fm);
if (save != null) {
save.setReplaceTarget(null);
@@ -529,13 +547,13 @@ public class DocumentsActivity extends Activity {
}
public void onStackPicked(DocumentStack stack) {
- mStack = stack;
+ mState.stack = stack;
onCurrentDirectoryChanged();
}
public void onRootPicked(RootInfo root, boolean closeDrawer) {
// Clear entire backstack and start in new root
- mStack.clear();
+ mState.stack.clear();
if (!mRoots.isRecentsRoot(root)) {
try {
@@ -566,19 +584,19 @@ public class DocumentsActivity extends Activity {
if (doc.isDirectory()) {
// TODO: query display mode user preference for this dir
if (doc.isGridPreferred()) {
- mDisplayState.mode = MODE_GRID;
+ mState.mode = MODE_GRID;
} else {
- mDisplayState.mode = MODE_LIST;
+ mState.mode = MODE_LIST;
}
- mStack.push(doc);
+ mState.stack.push(doc);
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
onFinished(doc.uri);
- } else if (mAction == ACTION_CREATE) {
+ } else if (mState.action == ACTION_CREATE) {
// Replace selected file
SaveFragment.get(fm).setReplaceTarget(doc);
- } else if (mAction == ACTION_MANAGE) {
+ } else if (mState.action == ACTION_MANAGE) {
// Open the document
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -592,7 +610,7 @@ public class DocumentsActivity extends Activity {
}
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 Uri[] uris = new Uri[size];
for (int i = 0; i < size; i++) {
@@ -629,14 +647,14 @@ public class DocumentsActivity extends Activity {
final ContentResolver resolver = getContentResolver();
final ContentValues values = new ContentValues();
- final String rawStack = DocumentStack.serialize(mStack);
- if (mAction == ACTION_CREATE) {
+ final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
+ if (mState.action == ACTION_CREATE) {
// Remember stack for last create
values.clear();
values.put(RecentsProvider.COL_PATH, rawStack);
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
for (Uri uri : uris) {
values.clear();
@@ -656,14 +674,14 @@ public class DocumentsActivity extends Activity {
intent.setData(uris[0]);
} else if (uris.length > 1) {
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++) {
clipData.addItem(new ClipData.Item(uris[i]));
}
intent.setClipData(clipData);
}
- if (mAction == ACTION_GET_CONTENT) {
+ if (mState.action == ACTION_GET_CONTENT) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -675,7 +693,7 @@ public class DocumentsActivity extends Activity {
finish();
}
- public static class DisplayState {
+ public static class State implements android.os.Parcelable {
public int action;
public int mode = MODE_LIST;
public String[] acceptMimes;
@@ -684,6 +702,11 @@ public class DocumentsActivity extends Activity {
public boolean showSize = 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_CREATE = 2;
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_LAST_MODIFIED = 1;
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() {
Log.d(TAG, "Current stack:");
- for (DocumentInfo doc : mStack) {
+ for (DocumentInfo doc : mState.stack) {
Log.d(TAG, "--> " + doc);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index f5d87a7..fd7293d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -47,7 +47,9 @@ import com.google.android.collect.Lists;
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.List;
@@ -138,12 +140,13 @@ public class RecentsCreateFragment extends Fragment {
uri, null, null, null, RecentsProvider.COL_TIMESTAMP + " DESC", signal);
try {
while (cursor != null && cursor.moveToNext()) {
- final String rawStack = cursor.getString(
+ final byte[] raw = cursor.getBlob(
cursor.getColumnIndex(RecentsProvider.COL_PATH));
try {
- final DocumentStack stack = DocumentStack.deserialize(resolver, rawStack);
+ final DocumentStack stack = new DocumentStack();
+ stack.read(new DataInputStream(new ByteArrayInputStream(raw)));
result.add(stack);
- } catch (FileNotFoundException e) {
+ } catch (IOException e) {
Log.w(TAG, "Failed to resolve stack: " + e);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 880a92b..f67c309 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -50,6 +50,8 @@ public class RootsCache {
// TODO: cache roots in local provider to avoid spinning up backends
// TODO: root updates should trigger UI refresh
+ private static final boolean RECENTS_ENABLED = false;
+
private final Context mContext;
public List<RootInfo> mRoots = Lists.newArrayList();
@@ -68,7 +70,7 @@ public class RootsCache {
public void update() {
mRoots.clear();
- {
+ if (RECENTS_ENABLED) {
// Create special root for recents
final RootInfo root = new RootInfo();
root.rootType = Root.ROOT_TYPE_SHORTCUT;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
index 2d73732..257c106 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
@@ -16,12 +16,14 @@
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.Cursor;
import android.provider.DocumentsContract.Document;
-import com.android.documentsui.DocumentsActivity.DisplayState;
-
/**
* Cursor wrapper that presents a sorted view of the underlying cursor. Handles
* common {@link Document} sorting modes, such as ordering directories first.
@@ -39,12 +41,12 @@ public class SortingCursorWrapper extends AbstractCursor {
final int count = cursor.getCount();
mPosition = new int[count];
switch (sortOrder) {
- case DisplayState.SORT_ORDER_DISPLAY_NAME:
+ case SORT_ORDER_DISPLAY_NAME:
mValueString = new String[count];
mValueLong = null;
break;
- case DisplayState.SORT_ORDER_LAST_MODIFIED:
- case DisplayState.SORT_ORDER_SIZE:
+ case SORT_ORDER_LAST_MODIFIED:
+ case SORT_ORDER_SIZE:
mValueString = null;
mValueLong = new long[count];
break;
@@ -63,7 +65,7 @@ public class SortingCursorWrapper extends AbstractCursor {
mPosition[i] = i;
switch (sortOrder) {
- case DisplayState.SORT_ORDER_DISPLAY_NAME:
+ case SORT_ORDER_DISPLAY_NAME:
final String mimeType = cursor.getString(mimeTypeIndex);
final String displayName = cursor.getString(displayNameIndex);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
@@ -72,24 +74,24 @@ public class SortingCursorWrapper extends AbstractCursor {
mValueString[i] = displayName;
}
break;
- case DisplayState.SORT_ORDER_LAST_MODIFIED:
+ case SORT_ORDER_LAST_MODIFIED:
mValueLong[i] = cursor.getLong(lastModifiedIndex);
break;
- case DisplayState.SORT_ORDER_SIZE:
+ case SORT_ORDER_SIZE:
mValueLong[i] = cursor.getLong(sizeIndex);
break;
}
}
switch (sortOrder) {
- case DisplayState.SORT_ORDER_DISPLAY_NAME:
+ case SORT_ORDER_DISPLAY_NAME:
synchronized (SortingCursorWrapper.class) {
binarySort(mPosition, mValueString);
}
break;
- case DisplayState.SORT_ORDER_LAST_MODIFIED:
- case DisplayState.SORT_ORDER_SIZE:
+ case SORT_ORDER_LAST_MODIFIED:
+ case SORT_ORDER_SIZE:
binarySort(mPosition, mValueLong);
break;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index d571971..feccadc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -30,13 +30,19 @@ import com.android.documentsui.RecentsProvider;
import libcore.io.IoUtils;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.ProtocolException;
import java.util.Comparator;
/**
* Representation of a {@link Document}.
*/
-public class DocumentInfo {
+public class DocumentInfo implements Durable {
+ private static final int VERSION_INIT = 1;
+
public Uri uri;
public String mimeType;
public String displayName;
@@ -46,6 +52,55 @@ public class DocumentInfo {
public long size;
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) {
final DocumentInfo doc = new DocumentInfo();
final String authority = parent.getAuthority();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
index b123a46..64631ab 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
@@ -16,54 +16,20 @@
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 org.json.JSONArray;
-import org.json.JSONException;
-
-import java.io.FileNotFoundException;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
import java.util.LinkedList;
/**
* Representation of a stack of {@link DocumentInfo}, usually the result of a
* user-driven traversal.
*/
-public class DocumentStack extends LinkedList<DocumentInfo> {
-
- 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 class DocumentStack extends LinkedList<DocumentInfo> implements Durable {
+ private static final int VERSION_INIT = 1;
public RootInfo getRoot(RootsCache roots) {
return roots.findRoot(getLast().uri);
@@ -78,4 +44,37 @@ public class DocumentStack extends LinkedList<DocumentInfo> {
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);
+ }
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/Durable.java b/packages/DocumentsUI/src/com/android/documentsui/model/Durable.java
new file mode 100644
index 0000000..01633ed
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/Durable.java
@@ -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;
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java b/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java
new file mode 100644
index 0000000..214fb14
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java
@@ -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;
+ }
+ }
+}