summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/DocumentsUI/res/drawable/item_background.xml24
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java2
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java259
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java85
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java164
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java119
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java12
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java30
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java42
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java172
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsCache.java6
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java117
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java28
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java128
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java2
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java26
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java29
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java13
-rw-r--r--packages/SystemUI/res/drawable-nodpi/lightning.pngbin2896 -> 0 bytes
-rw-r--r--packages/SystemUI/res/values/arrays.xml11
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/BatteryMeterView.java113
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java30
22 files changed, 984 insertions, 428 deletions
diff --git a/packages/DocumentsUI/res/drawable/item_background.xml b/packages/DocumentsUI/res/drawable/item_background.xml
index 4fb32fc..7447aa8 100644
--- a/packages/DocumentsUI/res/drawable/item_background.xml
+++ b/packages/DocumentsUI/res/drawable/item_background.xml
@@ -15,12 +15,20 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_activated="true" android:state_pressed="true" android:drawable="@*android:drawable/list_activated_holo" />
- <item android:state_activated="true" android:drawable="@*android:drawable/list_activated_holo" />
- <item android:state_focused="true" android:state_enabled="false" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_disabled_holo_light" />
- <item android:state_focused="true" android:state_enabled="false" android:drawable="@*android:drawable/list_selector_disabled_holo_light" />
- <item android:state_focused="true" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_background_transition_holo_light" />
- <item android:state_focused="false" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_background_transition_holo_light" />
- <item android:state_focused="true" android:drawable="@*android:drawable/list_focused_holo" />
- <item android:drawable="@android:color/transparent" />
+
+ <item android:state_enabled="false" android:state_selected="true" android:drawable="@*android:drawable/list_selector_disabled_holo_light" />
+ <item android:state_enabled="false" android:state_focused="true" android:drawable="@*android:drawable/list_selector_disabled_holo_light" />
+ <item android:state_enabled="false" android:state_pressed="true" android:drawable="@*android:drawable/list_selector_disabled_holo_light" />
+
+ <item android:state_activated="true" android:state_pressed="true" android:drawable="@*android:drawable/list_activated_holo" />
+ <item android:state_activated="true" android:drawable="@*android:drawable/list_activated_holo" />
+
+ <item android:state_focused="true" android:drawable="@*android:drawable/list_focused_holo" />
+ <item android:state_selected="true" android:drawable="@*android:drawable/list_focused_holo" />
+
+ <item android:state_pressed="true" android:state_focused="true" android:drawable="@*android:drawable/list_selector_background_transition_holo_light" />
+ <item android:state_pressed="true" android:drawable="@*android:drawable/list_selector_background_transition_holo_light" />
+
+ <item android:drawable="@android:color/transparent" />
+
</selector>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index e0b8d19..d8e60aa 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -70,7 +70,7 @@ public class CreateDirectoryFragment extends DialogFragment {
try {
final Uri childUri = DocumentsContract.createDocument(
- resolver, cwd.uri, Document.MIME_TYPE_DIR, displayName);
+ resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, displayName);
// Navigate into newly created child
final DocumentInfo childDoc = DocumentInfo.fromUri(resolver, childUri);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index e594437..45f028d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -20,6 +20,8 @@ import static com.android.documentsui.DocumentsActivity.TAG;
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.DocumentsActivity.State.MODE_UNKNOWN;
+import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN;
import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -29,6 +31,7 @@ import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ContentResolver;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
@@ -63,6 +66,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
import com.android.internal.util.Predicate;
@@ -91,43 +95,47 @@ public class DirectoryFragment extends Fragment {
private int mType = TYPE_NORMAL;
+ private int mLastMode = MODE_UNKNOWN;
+ private int mLastSortOrder = SORT_ORDER_UNKNOWN;
+
private Point mThumbSize;
private DocumentsAdapter mAdapter;
private LoaderCallbacks<DirectoryResult> mCallbacks;
private static final String EXTRA_TYPE = "type";
- private static final String EXTRA_AUTHORITY = "authority";
- private static final String EXTRA_ROOT_ID = "rootId";
- private static final String EXTRA_DOC_ID = "docId";
+ private static final String EXTRA_ROOT = "root";
+ private static final String EXTRA_DOC = "doc";
private static final String EXTRA_QUERY = "query";
- private static AtomicInteger sLoaderId = new AtomicInteger(4000);
+ /**
+ * MIME types that should always show thumbnails in list mode.
+ */
+ private static final String[] LIST_THUMBNAIL_MIMES = new String[] { "image/*", "video/*" };
- private int mLastSortOrder = -1;
+ private static AtomicInteger sLoaderId = new AtomicInteger(4000);
private final int mLoaderId = sLoaderId.incrementAndGet();
- public static void showNormal(FragmentManager fm, Uri uri) {
- show(fm, TYPE_NORMAL, uri.getAuthority(), null, DocumentsContract.getDocumentId(uri), null);
+ public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc) {
+ show(fm, TYPE_NORMAL, root, doc, null);
}
- public static void showSearch(FragmentManager fm, Uri uri, String query) {
- show(fm, TYPE_SEARCH, uri.getAuthority(), null, DocumentsContract.getDocumentId(uri),
- query);
+ public static void showSearch(
+ FragmentManager fm, RootInfo root, DocumentInfo doc, String query) {
+ show(fm, TYPE_SEARCH, root, doc, query);
}
public static void showRecentsOpen(FragmentManager fm) {
- show(fm, TYPE_RECENT_OPEN, null, null, null, null);
+ show(fm, TYPE_RECENT_OPEN, null, null, null);
}
- private static void show(FragmentManager fm, int type, String authority, String rootId,
- String docId, String query) {
+ private static void show(
+ FragmentManager fm, int type, RootInfo root, DocumentInfo doc, String query) {
final Bundle args = new Bundle();
args.putInt(EXTRA_TYPE, type);
- args.putString(EXTRA_AUTHORITY, authority);
- args.putString(EXTRA_ROOT_ID, rootId);
- args.putString(EXTRA_DOC_ID, docId);
+ args.putParcelable(EXTRA_ROOT, root);
+ args.putParcelable(EXTRA_DOC, doc);
args.putString(EXTRA_QUERY, query);
final DirectoryFragment fragment = new DirectoryFragment();
@@ -167,6 +175,7 @@ public class DirectoryFragment extends Fragment {
super.onActivityCreated(savedInstanceState);
final Context context = getActivity();
+ final State state = getDisplayState(DirectoryFragment.this);
mAdapter = new DocumentsAdapter();
mType = getArguments().getInt(EXTRA_TYPE);
@@ -174,35 +183,50 @@ public class DirectoryFragment extends Fragment {
mCallbacks = new LoaderCallbacks<DirectoryResult>() {
@Override
public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
- final State state = getDisplayState(DirectoryFragment.this);
-
- final String authority = getArguments().getString(EXTRA_AUTHORITY);
- final String rootId = getArguments().getString(EXTRA_ROOT_ID);
- final String docId = getArguments().getString(EXTRA_DOC_ID);
+ final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
+ final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
final String query = getArguments().getString(EXTRA_QUERY);
Uri contentsUri;
switch (mType) {
case TYPE_NORMAL:
- contentsUri = DocumentsContract.buildChildDocumentsUri(authority, docId);
- return new DirectoryLoader(context, rootId, contentsUri, state.sortOrder);
+ contentsUri = DocumentsContract.buildChildDocumentsUri(
+ doc.authority, doc.documentId);
+ return new DirectoryLoader(
+ context, root, doc, contentsUri, state.userSortOrder);
case TYPE_SEARCH:
contentsUri = DocumentsContract.buildSearchDocumentsUri(
- authority, docId, query);
- return new DirectoryLoader(context, rootId, contentsUri, state.sortOrder);
+ doc.authority, doc.documentId, query);
+ return new DirectoryLoader(
+ context, root, doc, contentsUri, state.userSortOrder);
case TYPE_RECENT_OPEN:
final RootsCache roots = DocumentsApplication.getRootsCache(context);
final List<RootInfo> matchingRoots = roots.getMatchingRoots(state);
- return new RecentLoader(context, matchingRoots);
+ return new RecentLoader(context, matchingRoots, state.acceptMimes);
default:
throw new IllegalStateException("Unknown type " + mType);
-
}
}
@Override
public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
+ if (!isAdded()) return;
+
mAdapter.swapCursor(result.cursor);
+
+ // Push latest state up to UI
+ // TODO: if mode change was racing with us, don't overwrite it
+ state.derivedMode = result.mode;
+ state.derivedSortOrder = result.sortOrder;
+ ((DocumentsActivity) context).onStateChanged();
+
+ updateDisplayState();
+
+ if (mLastSortOrder != state.derivedSortOrder) {
+ mLastSortOrder = state.derivedSortOrder;
+ mListView.smoothScrollToPosition(0);
+ mGridView.smoothScrollToPosition(0);
+ }
}
@Override
@@ -211,6 +235,9 @@ public class DirectoryFragment extends Fragment {
}
};
+ // Kick off loader at least once
+ getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
+
updateDisplayState();
}
@@ -220,22 +247,51 @@ public class DirectoryFragment extends Fragment {
updateDisplayState();
}
- public void updateDisplayState() {
+ public void onUserSortOrderChanged() {
+ // Sort order change always triggers reload; we'll trigger state change
+ // on the flip side.
+ getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
+ }
+
+ public void onUserModeChanged() {
+ final ContentResolver resolver = getActivity().getContentResolver();
final State state = getDisplayState(this);
- if (mLastSortOrder != state.sortOrder) {
- getLoaderManager().restartLoader(mLoaderId, null, mCallbacks);
- mLastSortOrder = state.sortOrder;
- }
+ final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
+ final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
- mListView.smoothScrollToPosition(0);
- mGridView.smoothScrollToPosition(0);
+ final Uri stateUri = RecentsProvider.buildState(
+ root.authority, root.rootId, doc.documentId);
+ final ContentValues values = new ContentValues();
+ values.put(StateColumns.MODE, state.userMode);
- mListView.setVisibility(state.mode == MODE_LIST ? View.VISIBLE : View.GONE);
- mGridView.setVisibility(state.mode == MODE_GRID ? View.VISIBLE : View.GONE);
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ resolver.insert(stateUri, values);
+ return null;
+ }
+ }.execute();
+
+ // Mode change is just visual change; no need to kick loader, and
+ // deliver change event immediately.
+ state.derivedMode = state.userMode;
+ ((DocumentsActivity) getActivity()).onStateChanged();
+
+ updateDisplayState();
+ }
+
+ private void updateDisplayState() {
+ final State state = getDisplayState(this);
mFilter = new MimePredicate(state.acceptMimes);
+ if (mLastMode == state.derivedMode) return;
+ mLastMode = state.derivedMode;
+
+ mListView.setVisibility(state.derivedMode == MODE_LIST ? View.VISIBLE : View.GONE);
+ mGridView.setVisibility(state.derivedMode == MODE_GRID ? View.VISIBLE : View.GONE);
+
final int choiceMode;
if (state.allowMultiple) {
choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
@@ -244,7 +300,7 @@ public class DirectoryFragment extends Fragment {
}
final int thumbSize;
- if (state.mode == MODE_GRID) {
+ if (state.derivedMode == MODE_GRID) {
thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width);
mListView.setAdapter(null);
mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
@@ -253,15 +309,15 @@ public class DirectoryFragment extends Fragment {
mGridView.setNumColumns(GridView.AUTO_FIT);
mGridView.setChoiceMode(choiceMode);
mCurrentView = mGridView;
- } else if (state.mode == MODE_LIST) {
- thumbSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
+ } else if (state.derivedMode == MODE_LIST) {
+ thumbSize = getResources().getDimensionPixelSize(R.dimen.icon_size);
mGridView.setAdapter(null);
mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
mListView.setAdapter(mAdapter);
mListView.setChoiceMode(choiceMode);
mCurrentView = mListView;
} else {
- throw new IllegalStateException();
+ throw new IllegalStateException("Unknown state " + state.derivedMode);
}
mThumbSize = new Point(thumbSize, thumbSize);
@@ -271,9 +327,11 @@ public class DirectoryFragment extends Fragment {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final Cursor cursor = mAdapter.getItem(position);
- final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
- if (mFilter.apply(doc)) {
- ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
+ if (cursor != null) {
+ final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
+ if (mFilter.apply(doc)) {
+ ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
+ }
}
}
};
@@ -344,10 +402,20 @@ public class DirectoryFragment extends Fragment {
public void onItemCheckedStateChanged(
ActionMode mode, int position, long id, boolean checked) {
if (checked) {
- // Directories cannot be checked
+ // Directories and footer items cannot be checked
+ boolean valid = false;
+
final Cursor cursor = mAdapter.getItem(position);
- final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- if (Document.MIME_TYPE_DIR.equals(docMimeType)) {
+ if (cursor != null) {
+ final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+
+ // Only valid if non-directory matches filter
+ final State state = getDisplayState(DirectoryFragment.this);
+ valid = !Document.MIME_TYPE_DIR.equals(docMimeType)
+ && MimePredicate.mimeMatches(state.acceptMimes, docMimeType);
+ }
+
+ if (!valid) {
mCurrentView.setItemChecked(position, false);
}
}
@@ -366,7 +434,7 @@ public class DirectoryFragment extends Fragment {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setType(doc.mimeType);
- intent.putExtra(Intent.EXTRA_STREAM, doc.uri);
+ intent.putExtra(Intent.EXTRA_STREAM, doc.derivedUri);
} else if (docs.size() > 1) {
intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
@@ -377,7 +445,7 @@ public class DirectoryFragment extends Fragment {
final ArrayList<Uri> uris = Lists.newArrayList();
for (DocumentInfo doc : docs) {
mimeTypes.add(doc.mimeType);
- uris.add(doc.uri);
+ uris.add(doc.derivedUri);
}
intent.setType(findCommonMimeType(mimeTypes));
@@ -403,7 +471,7 @@ public class DirectoryFragment extends Fragment {
continue;
}
- if (!DocumentsContract.deleteDocument(resolver, doc.uri)) {
+ if (!DocumentsContract.deleteDocument(resolver, doc.derivedUri)) {
Log.w(TAG, "Failed to delete " + doc);
hadTrouble = true;
}
@@ -418,11 +486,25 @@ public class DirectoryFragment extends Fragment {
return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
}
- private interface Footer {
- public View getView(View convertView, ViewGroup parent);
+ private static abstract class Footer {
+ private final int mItemViewType;
+
+ public Footer(int itemViewType) {
+ mItemViewType = itemViewType;
+ }
+
+ public abstract View getView(View convertView, ViewGroup parent);
+
+ public int getItemViewType() {
+ return mItemViewType;
+ }
}
- private static class LoadingFooter implements Footer {
+ private static class LoadingFooter extends Footer {
+ public LoadingFooter() {
+ super(1);
+ }
+
@Override
public View getView(View convertView, ViewGroup parent) {
final Context context = parent.getContext();
@@ -434,11 +516,12 @@ public class DirectoryFragment extends Fragment {
}
}
- private class MessageFooter implements Footer {
+ private class MessageFooter extends Footer {
private final int mIcon;
private final String mMessage;
- public MessageFooter(int icon, String message) {
+ public MessageFooter(int itemViewType, int icon, String message) {
+ super(itemViewType);
mIcon = icon;
mMessage = message;
}
@@ -450,9 +533,9 @@ public class DirectoryFragment extends Fragment {
if (convertView == null) {
final LayoutInflater inflater = LayoutInflater.from(context);
- if (state.mode == MODE_LIST) {
+ if (state.derivedMode == MODE_LIST) {
convertView = inflater.inflate(R.layout.item_message_list, parent, false);
- } else if (state.mode == MODE_GRID) {
+ } else if (state.derivedMode == MODE_GRID) {
convertView = inflater.inflate(R.layout.item_message_grid, parent, false);
} else {
throw new IllegalStateException();
@@ -483,11 +566,11 @@ public class DirectoryFragment extends Fragment {
if (extras != null) {
final String info = extras.getString(DocumentsContract.EXTRA_INFO);
if (info != null) {
- mFooters.add(new MessageFooter(R.drawable.ic_dialog_alert, info));
+ mFooters.add(new MessageFooter(2, R.drawable.ic_dialog_alert, info));
}
final String error = extras.getString(DocumentsContract.EXTRA_ERROR);
if (error != null) {
- mFooters.add(new MessageFooter(R.drawable.ic_dialog_alert, error));
+ mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, error));
}
if (extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)) {
mFooters.add(new LoadingFooter());
@@ -509,7 +592,11 @@ public class DirectoryFragment extends Fragment {
return getDocumentView(position, convertView, parent);
} else {
position -= mCursorCount;
- return mFooters.get(position).getView(convertView, parent);
+ convertView = mFooters.get(position).getView(convertView, parent);
+ // Only the view itself is disabled; contents inside shouldn't
+ // be dimmed.
+ convertView.setEnabled(false);
+ return convertView;
}
}
@@ -523,9 +610,9 @@ public class DirectoryFragment extends Fragment {
if (convertView == null) {
final LayoutInflater inflater = LayoutInflater.from(context);
- if (state.mode == MODE_LIST) {
+ if (state.derivedMode == MODE_LIST) {
convertView = inflater.inflate(R.layout.item_doc_list, parent, false);
- } else if (state.mode == MODE_GRID) {
+ } else if (state.derivedMode == MODE_GRID) {
convertView = inflater.inflate(R.layout.item_doc_grid, parent, false);
} else {
throw new IllegalStateException();
@@ -558,7 +645,11 @@ public class DirectoryFragment extends Fragment {
oldTask.cancel(false);
}
- if ((docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0) {
+ final boolean supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
+ final boolean allowThumbnail = (state.derivedMode == MODE_GRID)
+ || MimePredicate.mimeMatches(LIST_THUMBNAIL_MIMES, docMimeType);
+
+ if (supportsThumbnail && allowThumbnail) {
final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
final Bitmap cachedResult = thumbs.get(uri);
if (cachedResult != null) {
@@ -567,7 +658,7 @@ public class DirectoryFragment extends Fragment {
final ThumbnailAsyncTask task = new ThumbnailAsyncTask(icon, mThumbSize);
icon.setImageBitmap(null);
icon.setTag(task);
- task.execute(uri);
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, uri);
}
} else if (docIcon != 0) {
icon.setImageDrawable(IconUtils.loadPackageIcon(context, docAuthority, docIcon));
@@ -619,6 +710,18 @@ public class DirectoryFragment extends Fragment {
line2.setVisibility(hasLine2 ? View.VISIBLE : View.GONE);
+ final boolean enabled = Document.MIME_TYPE_DIR.equals(docMimeType)
+ || MimePredicate.mimeMatches(state.acceptMimes, docMimeType);
+ if (enabled) {
+ setEnabledRecursive(convertView, true);
+ icon.setAlpha(1f);
+ icon1.setAlpha(1f);
+ } else {
+ setEnabledRecursive(convertView, false);
+ icon.setAlpha(0.5f);
+ icon1.setAlpha(0.5f);
+ }
+
return convertView;
}
@@ -643,23 +746,19 @@ public class DirectoryFragment extends Fragment {
}
@Override
+ public int getViewTypeCount() {
+ return 4;
+ }
+
+ @Override
public int getItemViewType(int position) {
if (position < mCursorCount) {
return 0;
} else {
- return IGNORE_ITEM_VIEW_TYPE;
+ position -= mCursorCount;
+ return mFooters.get(position).getItemViewType();
}
}
-
- @Override
- public boolean areAllItemsEnabled() {
- return false;
- }
-
- @Override
- public boolean isEnabled(int position) {
- return position < mCursorCount;
- }
}
private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> {
@@ -749,4 +848,16 @@ public class DirectoryFragment extends Fragment {
return commonType[0] + "/" + commonType[1];
}
+
+ private void setEnabledRecursive(View v, boolean enabled) {
+ if (v.isEnabled() == enabled) return;
+ v.setEnabled(enabled);
+
+ if (v instanceof ViewGroup) {
+ final ViewGroup vg = (ViewGroup) v;
+ for (int i = vg.getChildCount() - 1; i >= 0; i--) {
+ setEnabledRecursive(vg.getChildAt(i), enabled);
+ }
+ }
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 6ea57d7..1471836 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -16,18 +16,29 @@
package com.android.documentsui;
+import static com.android.documentsui.DocumentsActivity.TAG;
+import static com.android.documentsui.DocumentsActivity.State.MODE_UNKNOWN;
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 static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN;
+import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import android.content.AsyncTaskLoader;
import android.content.ContentProviderClient;
+import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.provider.DocumentsContract.Document;
+import android.util.Log;
+
+import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.RecentsProvider.StateColumns;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.RootInfo;
import libcore.io.IoUtils;
@@ -36,6 +47,9 @@ class DirectoryResult implements AutoCloseable {
Cursor cursor;
Exception exception;
+ int mode = MODE_UNKNOWN;
+ int sortOrder = SORT_ORDER_UNKNOWN;
+
@Override
public void close() {
IoUtils.closeQuietly(cursor);
@@ -48,18 +62,21 @@ class DirectoryResult implements AutoCloseable {
public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
- private final String mRootId;
+ private final RootInfo mRoot;
+ private final DocumentInfo mDoc;
private final Uri mUri;
- private final int mSortOrder;
+ private final int mUserSortOrder;
private CancellationSignal mSignal;
private DirectoryResult mResult;
- public DirectoryLoader(Context context, String rootId, Uri uri, int sortOrder) {
+ public DirectoryLoader(
+ Context context, RootInfo root, DocumentInfo doc, Uri uri, int userSortOrder) {
super(context);
- mRootId = rootId;
+ mRoot = root;
+ mDoc = doc;
mUri = uri;
- mSortOrder = sortOrder;
+ mUserSortOrder = userSortOrder;
}
@Override
@@ -70,20 +87,63 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
}
mSignal = new CancellationSignal();
}
- final DirectoryResult result = new DirectoryResult();
+
+ final ContentResolver resolver = getContext().getContentResolver();
final String authority = mUri.getAuthority();
+
+ final DirectoryResult result = new DirectoryResult();
+
+ int userMode = State.MODE_UNKNOWN;
+
+ // Pick up any custom modes requested by user
+ Cursor cursor = null;
try {
- result.client = getContext()
- .getContentResolver().acquireUnstableContentProviderClient(authority);
- final Cursor cursor = result.client.query(
- mUri, null, null, null, getQuerySortOrder(mSortOrder), mSignal);
+ final Uri stateUri = RecentsProvider.buildState(
+ mRoot.authority, mRoot.rootId, mDoc.documentId);
+ cursor = resolver.query(stateUri, null, null, null, null);
+ if (cursor.moveToFirst()) {
+ userMode = getCursorInt(cursor, StateColumns.MODE);
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+
+ if (userMode != State.MODE_UNKNOWN) {
+ result.mode = userMode;
+ } else {
+ if ((mDoc.flags & Document.FLAG_DIR_PREFERS_GRID) != 0) {
+ result.mode = State.MODE_GRID;
+ } else {
+ result.mode = State.MODE_LIST;
+ }
+ }
+
+ if (mUserSortOrder != State.SORT_ORDER_UNKNOWN) {
+ result.sortOrder = mUserSortOrder;
+ } else {
+ if ((mDoc.flags & Document.FLAG_DIR_PREFERS_LAST_MODIFIED) != 0) {
+ result.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
+ } else {
+ result.sortOrder = State.SORT_ORDER_DISPLAY_NAME;
+ }
+ }
+
+ Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + mUserSortOrder + " --> mode="
+ + result.mode + ", sortOrder=" + result.sortOrder);
+
+ try {
+ result.client = resolver.acquireUnstableContentProviderClient(authority);
+ cursor = result.client.query(
+ mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
cursor.registerContentObserver(mObserver);
- final Cursor withRoot = new RootCursorWrapper(mUri.getAuthority(), mRootId, cursor, -1);
- final Cursor sorted = new SortingCursorWrapper(withRoot, mSortOrder);
+ final Cursor withRoot = new RootCursorWrapper(
+ mUri.getAuthority(), mRoot.rootId, cursor, -1);
+ final Cursor sorted = new SortingCursorWrapper(withRoot, result.sortOrder);
result.cursor = sorted;
} catch (Exception e) {
+ Log.d(TAG, "Failed to query", e);
result.exception = e;
ContentProviderClient.closeQuietly(result.client);
} finally {
@@ -91,6 +151,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
mSignal = null;
}
}
+
return result;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 38b2ee8..79d2443 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -60,6 +60,9 @@ import android.widget.SearchView.OnQueryTextListener;
import android.widget.TextView;
import android.widget.Toast;
+import com.android.documentsui.RecentsProvider.RecentColumns;
+import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
@@ -168,10 +171,6 @@ public class DocumentsActivity extends Activity {
mState.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(this);
if (mState.action == ACTION_MANAGE) {
- mState.sortOrder = SORT_ORDER_LAST_MODIFIED;
- }
-
- if (mState.action == ACTION_MANAGE) {
final Uri uri = intent.getData();
final String rootId = DocumentsContract.getRootId(uri);
final RootInfo root = mRoots.getRoot(uri.getAuthority(), rootId);
@@ -191,7 +190,7 @@ public class DocumentsActivity extends Activity {
try {
if (cursor.moveToFirst()) {
final byte[] rawStack = cursor.getBlob(
- cursor.getColumnIndex(RecentsProvider.COL_PATH));
+ cursor.getColumnIndex(ResumeColumns.STACK));
DurableUtils.readFromArray(rawStack, mState.stack);
}
} catch (IOException e) {
@@ -204,7 +203,7 @@ public class DocumentsActivity extends Activity {
final RootInfo root = getCurrentRoot();
final List<RootInfo> matchingRoots = mRoots.getMatchingRoots(mState);
if (!matchingRoots.contains(root)) {
- mState.stack.clear();
+ mState.stack.reset();
}
// Only open drawer when showing recents
@@ -343,11 +342,16 @@ 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(mState.mode != MODE_GRID);
- list.setVisible(mState.mode != MODE_LIST);
+ if (cwd != null) {
+ sort.setVisible(true);
+ grid.setVisible(mState.derivedMode != MODE_GRID);
+ list.setVisible(mState.derivedMode != MODE_LIST);
+ } else {
+ sort.setVisible(false);
+ grid.setVisible(false);
+ list.setVisible(false);
+ }
- // No sorting in recents
- sort.setVisible(cwd != null);
// Only sort by size when visible
sortSize.setVisible(mState.showSize);
@@ -392,28 +396,19 @@ public class DocumentsActivity extends Activity {
} else if (id == R.id.menu_search) {
return false;
} else if (id == R.id.menu_sort_name) {
- mState.sortOrder = State.SORT_ORDER_DISPLAY_NAME;
- updateDisplayState();
+ setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
return true;
} else if (id == R.id.menu_sort_date) {
- mState.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
- updateDisplayState();
+ setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
return true;
} else if (id == R.id.menu_sort_size) {
- mState.sortOrder = State.SORT_ORDER_SIZE;
- updateDisplayState();
+ setUserSortOrder(State.SORT_ORDER_SIZE);
return true;
} else if (id == R.id.menu_grid) {
- // TODO: persist explicit user mode for cwd
- mState.mode = MODE_GRID;
- updateDisplayState();
- invalidateOptionsMenu();
+ setUserMode(State.MODE_GRID);
return true;
} else if (id == R.id.menu_list) {
- // TODO: persist explicit user mode for cwd
- mState.mode = MODE_LIST;
- updateDisplayState();
- invalidateOptionsMenu();
+ setUserMode(State.MODE_LIST);
return true;
} else if (id == R.id.menu_settings) {
startActivity(new Intent(this, SettingsActivity.class));
@@ -423,8 +418,36 @@ public class DocumentsActivity extends Activity {
}
}
+ /**
+ * Update UI to reflect internal state changes not from user.
+ */
+ public void onStateChanged() {
+ invalidateOptionsMenu();
+ }
+
+ /**
+ * Set state sort order based on explicit user action.
+ */
+ private void setUserSortOrder(int sortOrder) {
+ mState.userSortOrder = sortOrder;
+ DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
+ }
+
+ /**
+ * Set state mode based on explicit user action.
+ */
+ private void setUserMode(int mode) {
+ mState.userMode = mode;
+ DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
+ }
+
@Override
public void onBackPressed() {
+ if (!mState.stackTouched) {
+ super.onBackPressed();
+ return;
+ }
+
final int size = mState.stack.size();
if (size > 1) {
mState.stack.pop();
@@ -520,6 +543,7 @@ public class DocumentsActivity extends Activity {
}
while (mState.stack.size() > itemPosition + 1) {
+ mState.stackTouched = true;
mState.stack.pop();
}
onCurrentDirectoryChanged();
@@ -528,8 +552,8 @@ public class DocumentsActivity extends Activity {
};
public RootInfo getCurrentRoot() {
- if (mState.stack.size() > 0) {
- return mState.stack.getRoot(mRoots);
+ if (mState.stack.root != null) {
+ return mState.stack.root;
} else {
return mRoots.getRecentsRoot();
}
@@ -545,6 +569,7 @@ public class DocumentsActivity extends Activity {
private void onCurrentDirectoryChanged() {
final FragmentManager fm = getFragmentManager();
+ final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
if (cwd == null) {
@@ -557,10 +582,10 @@ public class DocumentsActivity extends Activity {
} else {
if (mState.currentSearch != null) {
// Ongoing search
- DirectoryFragment.showSearch(fm, cwd.uri, mState.currentSearch);
+ DirectoryFragment.showSearch(fm, root, cwd, mState.currentSearch);
} else {
// Normal boring directory
- DirectoryFragment.showNormal(fm, cwd.uri);
+ DirectoryFragment.showNormal(fm, root, cwd);
}
}
@@ -582,19 +607,17 @@ public class DocumentsActivity extends Activity {
dumpStack();
}
- private void updateDisplayState() {
- // TODO: handle multiple directory stacks on tablets
- DirectoryFragment.get(getFragmentManager()).updateDisplayState();
- }
-
public void onStackPicked(DocumentStack stack) {
mState.stack = stack;
+ mState.stackTouched = true;
onCurrentDirectoryChanged();
}
public void onRootPicked(RootInfo root, boolean closeDrawer) {
// Clear entire backstack and start in new root
+ mState.stack.root = root;
mState.stack.clear();
+ mState.stackTouched = true;
if (!mRoots.isRecentsRoot(root)) {
try {
@@ -623,17 +646,12 @@ public class DocumentsActivity extends Activity {
public void onDocumentPicked(DocumentInfo doc) {
final FragmentManager fm = getFragmentManager();
if (doc.isDirectory()) {
- // TODO: query display mode user preference for this dir
- if (doc.isGridPreferred()) {
- mState.mode = MODE_GRID;
- } else {
- mState.mode = MODE_LIST;
- }
mState.stack.push(doc);
+ mState.stackTouched = true;
onCurrentDirectoryChanged();
} else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
// Explicit file picked, return
- onFinished(doc.uri);
+ onFinished(doc.derivedUri);
} else if (mState.action == ACTION_CREATE) {
// Replace selected file
SaveFragment.get(fm).setReplaceTarget(doc);
@@ -641,7 +659,7 @@ public class DocumentsActivity extends Activity {
// First try managing the document; we expect manager to filter
// based on authority, so we don't grant.
final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
- manage.setData(doc.uri);
+ manage.setData(doc.derivedUri);
try {
startActivity(manage);
@@ -649,7 +667,7 @@ public class DocumentsActivity extends Activity {
// Fall back to viewing
final Intent view = new Intent(Intent.ACTION_VIEW);
view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- view.setData(doc.uri);
+ view.setData(doc.derivedUri);
try {
startActivity(view);
@@ -665,22 +683,21 @@ public class DocumentsActivity extends Activity {
final int size = docs.size();
final Uri[] uris = new Uri[size];
for (int i = 0; i < size; i++) {
- uris[i] = docs.get(i).uri;
+ uris[i] = docs.get(i).derivedUri;
}
onFinished(uris);
}
}
public void onSaveRequested(DocumentInfo replaceTarget) {
- onFinished(replaceTarget.uri);
+ onFinished(replaceTarget.derivedUri);
}
public void onSaveRequested(String mimeType, String displayName) {
final DocumentInfo cwd = getCurrentDirectory();
- final String authority = cwd.uri.getAuthority();
final Uri childUri = DocumentsContract.createDocument(
- getContentResolver(), cwd.uri, mimeType, displayName);
+ getContentResolver(), cwd.derivedUri, mimeType, displayName);
if (childUri != null) {
onFinished(childUri);
} else {
@@ -698,22 +715,14 @@ public class DocumentsActivity extends Activity {
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 (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
- // Remember opened items
- for (Uri uri : uris) {
- values.clear();
- values.put(RecentsProvider.COL_URI, uri.toString());
- resolver.insert(RecentsProvider.buildRecentOpen(), values);
- }
+ values.put(RecentColumns.STACK, rawStack);
+ resolver.insert(RecentsProvider.buildRecent(), values);
}
// Remember location for next app launch
final String packageName = getCallingPackage();
values.clear();
- values.put(RecentsProvider.COL_PATH, rawStack);
+ values.put(ResumeColumns.STACK, rawStack);
resolver.insert(RecentsProvider.buildResume(packageName), values);
final Intent intent = new Intent();
@@ -742,13 +751,23 @@ public class DocumentsActivity extends Activity {
public static class State implements android.os.Parcelable {
public int action;
- public int mode = MODE_LIST;
public String[] acceptMimes;
- public int sortOrder = SORT_ORDER_DISPLAY_NAME;
+
+ /** Explicit user choice */
+ public int userMode = MODE_UNKNOWN;
+ /** Derived after loader */
+ public int derivedMode = MODE_LIST;
+
+ /** Explicit user choice */
+ public int userSortOrder = SORT_ORDER_UNKNOWN;
+ /** Derived after loader */
+ public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
+
public boolean allowMultiple = false;
public boolean showSize = false;
public boolean localOnly = false;
public boolean showAdvanced = false;
+ public boolean stackTouched = false;
/** Current user navigation stack; empty implies recents. */
public DocumentStack stack = new DocumentStack();
@@ -760,12 +779,14 @@ public class DocumentsActivity extends Activity {
public static final int ACTION_GET_CONTENT = 3;
public static final int ACTION_MANAGE = 4;
- public static final int MODE_LIST = 0;
- public static final int MODE_GRID = 1;
+ public static final int MODE_UNKNOWN = 0;
+ public static final int MODE_LIST = 1;
+ public static final int MODE_GRID = 2;
- 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;
+ public static final int SORT_ORDER_UNKNOWN = 0;
+ public static final int SORT_ORDER_DISPLAY_NAME = 1;
+ public static final int SORT_ORDER_LAST_MODIFIED = 2;
+ public static final int SORT_ORDER_SIZE = 3;
@Override
public int describeContents() {
@@ -775,13 +796,14 @@ public class DocumentsActivity extends Activity {
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(action);
- out.writeInt(mode);
+ out.writeInt(userMode);
out.writeStringArray(acceptMimes);
- out.writeInt(sortOrder);
+ out.writeInt(userSortOrder);
out.writeInt(allowMultiple ? 1 : 0);
out.writeInt(showSize ? 1 : 0);
out.writeInt(localOnly ? 1 : 0);
out.writeInt(showAdvanced ? 1 : 0);
+ out.writeInt(stackTouched ? 1 : 0);
DurableUtils.writeToParcel(out, stack);
out.writeString(currentSearch);
}
@@ -791,13 +813,14 @@ public class DocumentsActivity extends Activity {
public State createFromParcel(Parcel in) {
final State state = new State();
state.action = in.readInt();
- state.mode = in.readInt();
+ state.userMode = in.readInt();
state.acceptMimes = in.readStringArray();
- state.sortOrder = in.readInt();
+ state.userSortOrder = in.readInt();
state.allowMultiple = in.readInt() != 0;
state.showSize = in.readInt() != 0;
state.localOnly = in.readInt() != 0;
state.showAdvanced = in.readInt() != 0;
+ state.stackTouched = in.readInt() != 0;
DurableUtils.readFromParcel(in, state.stack);
state.currentSearch = in.readString();
return state;
@@ -811,9 +834,10 @@ public class DocumentsActivity extends Activity {
}
private void dumpStack() {
- Log.d(TAG, "Current stack:");
+ Log.d(TAG, "Current stack: ");
+ Log.d(TAG, " * " + mState.stack.root);
for (DocumentInfo doc : mState.stack) {
- Log.d(TAG, "--> " + doc);
+ Log.d(TAG, " +-- " + doc);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
new file mode 100644
index 0000000..60f0103
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
@@ -0,0 +1,119 @@
+/*
+ * 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;
+
+import static com.android.documentsui.DocumentsActivity.TAG;
+
+import android.database.AbstractCursor;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.DocumentsContract.Document;
+import android.util.Log;
+
+/**
+ * Cursor wrapper that filters MIME types not matching given list.
+ */
+public class FilteringCursorWrapper extends AbstractCursor {
+ private final Cursor mCursor;
+
+ private final int[] mPosition;
+ private int mCount;
+
+ public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes) {
+ mCursor = cursor;
+
+ final int count = cursor.getCount();
+ mPosition = new int[count];
+
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ final String mimeType = cursor.getString(
+ cursor.getColumnIndex(Document.COLUMN_MIME_TYPE));
+ if (MimePredicate.mimeMatches(acceptMimes, mimeType)) {
+ mPosition[mCount++] = cursor.getPosition();
+ }
+ }
+
+ Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount);
+ }
+
+ @Override
+ public Bundle getExtras() {
+ return mCursor.getExtras();
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ mCursor.close();
+ }
+
+ @Override
+ public boolean onMove(int oldPosition, int newPosition) {
+ return mCursor.moveToPosition(mPosition[newPosition]);
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ return mCursor.getColumnNames();
+ }
+
+ @Override
+ public int getCount() {
+ return mCount;
+ }
+
+ @Override
+ public double getDouble(int column) {
+ return mCursor.getDouble(column);
+ }
+
+ @Override
+ public float getFloat(int column) {
+ return mCursor.getFloat(column);
+ }
+
+ @Override
+ public int getInt(int column) {
+ return mCursor.getInt(column);
+ }
+
+ @Override
+ public long getLong(int column) {
+ return mCursor.getLong(column);
+ }
+
+ @Override
+ public short getShort(int column) {
+ return mCursor.getShort(column);
+ }
+
+ @Override
+ public String getString(int column) {
+ return mCursor.getString(column);
+ }
+
+ @Override
+ public int getType(int column) {
+ return mCursor.getType(column);
+ }
+
+ @Override
+ public boolean isNull(int column) {
+ return mCursor.isNull(column);
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
index 85d0988..b55ce82 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
@@ -49,6 +49,18 @@ public class MimePredicate implements Predicate<DocumentInfo> {
return false;
}
+ public static boolean mimeMatches(String filter, String[] tests) {
+ if (tests == null) {
+ return true;
+ }
+ for (String test : tests) {
+ if (mimeMatches(filter, test)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public static boolean mimeMatches(String[] filters, String test) {
if (filters == null) {
return true;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index 756a297..57442a0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -17,6 +17,9 @@
package com.android.documentsui;
import static com.android.documentsui.DocumentsActivity.TAG;
+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.content.AsyncTaskLoader;
import android.content.ContentProviderClient;
@@ -79,6 +82,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
}
private final List<RootInfo> mRoots;
+ private final String[] mAcceptMimes;
private final HashMap<RootInfo, RecentTask> mTasks = Maps.newHashMap();
@@ -135,9 +139,10 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
}
}
- public RecentLoader(Context context, List<RootInfo> roots) {
+ public RecentLoader(Context context, List<RootInfo> roots, String[] acceptMimes) {
super(context);
mRoots = roots;
+ mAcceptMimes = acceptMimes;
}
@Override
@@ -171,7 +176,15 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
for (RecentTask task : mTasks.values()) {
if (task.isDone()) {
try {
- cursors.add(task.get());
+ final Cursor cursor = task.get();
+ final FilteringCursorWrapper filtered = new FilteringCursorWrapper(
+ cursor, mAcceptMimes) {
+ @Override
+ public void close() {
+ // Ignored, since we manage cursor lifecycle internally
+ }
+ };
+ cursors.add(filtered);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
@@ -181,15 +194,14 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
}
final DirectoryResult result = new DirectoryResult();
+
+ final boolean acceptImages = MimePredicate.mimeMatches("image/*", mAcceptMimes);
+ result.mode = acceptImages ? MODE_GRID : MODE_LIST;
+ result.sortOrder = SORT_ORDER_LAST_MODIFIED;
+
if (cursors.size() > 0) {
final MergeCursor merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
- final SortingCursorWrapper sorted = new SortingCursorWrapper(
- merged, State.SORT_ORDER_LAST_MODIFIED) {
- @Override
- public void close() {
- // Ignored, since we manage cursor lifecycle internally
- }
- };
+ final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder);
result.cursor = sorted;
}
return result;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 461c415..3642478 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -26,10 +26,14 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Loader;
import android.database.Cursor;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
import android.text.TextUtils.TruncateAt;
+import android.text.style.ImageSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -41,8 +45,8 @@ import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+import com.android.documentsui.RecentsProvider.RecentColumns;
import com.android.documentsui.model.DocumentStack;
-import com.android.documentsui.model.RootInfo;
import com.google.android.collect.Lists;
import libcore.io.IoUtils;
@@ -128,7 +132,7 @@ public class RecentsCreateFragment extends Fragment {
public static class RecentsCreateLoader extends UriDerivativeLoader<Uri, List<DocumentStack>> {
public RecentsCreateLoader(Context context) {
- super(context, RecentsProvider.buildRecentCreate());
+ super(context, RecentsProvider.buildRecent());
}
@Override
@@ -137,14 +141,14 @@ public class RecentsCreateFragment extends Fragment {
final ContentResolver resolver = getContext().getContentResolver();
final Cursor cursor = resolver.query(
- uri, null, null, null, RecentsProvider.COL_TIMESTAMP + " DESC", signal);
+ uri, null, null, null, RecentColumns.TIMESTAMP + " DESC", signal);
try {
while (cursor != null && cursor.moveToNext()) {
- final byte[] raw = cursor.getBlob(
- cursor.getColumnIndex(RecentsProvider.COL_PATH));
+ final byte[] rawStack = cursor.getBlob(
+ cursor.getColumnIndex(RecentColumns.STACK));
try {
final DocumentStack stack = new DocumentStack();
- stack.read(new DataInputStream(new ByteArrayInputStream(raw)));
+ stack.read(new DataInputStream(new ByteArrayInputStream(rawStack)));
result.add(stack);
} catch (IOException e) {
Log.w(TAG, "Failed to resolve stack: " + e);
@@ -181,21 +185,29 @@ public class RecentsCreateFragment extends Fragment {
final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+ final View line2 = convertView.findViewById(R.id.line2);
final DocumentStack stack = getItem(position);
- final RootInfo root = stack.getRoot(roots);
- icon.setImageDrawable(root.loadIcon(context));
+ icon.setImageDrawable(stack.root.loadIcon(context));
- final StringBuilder builder = new StringBuilder();
- for (int i = stack.size() - 1; i >= 0; i--) {
+ final Drawable crumb = context.getResources()
+ .getDrawable(R.drawable.ic_breadcrumb_arrow);
+ crumb.setBounds(0, 0, crumb.getIntrinsicWidth(), crumb.getIntrinsicHeight());
+
+ final SpannableStringBuilder builder = new SpannableStringBuilder();
+ builder.append(stack.root.title);
+ appendDrawable(builder, crumb);
+ for (int i = stack.size() - 2; i >= 0; i--) {
builder.append(stack.get(i).displayName);
if (i > 0) {
- builder.append(" \u232a ");
+ appendDrawable(builder, crumb);
}
}
- title.setText(builder.toString());
+ title.setText(builder);
title.setEllipsize(TruncateAt.MIDDLE);
+ line2.setVisibility(View.GONE);
+
return convertView;
}
@@ -214,4 +226,10 @@ public class RecentsCreateFragment extends Fragment {
return getItem(position).hashCode();
}
}
+
+ private static void appendDrawable(SpannableStringBuilder b, Drawable d) {
+ final int length = b.length();
+ b.append("\u232a");
+ b.setSpan(new ImageSpan(d), length, b.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index 0c87783..1fe5d54 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -25,51 +25,64 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
import android.text.format.DateUtils;
import android.util.Log;
public class RecentsProvider extends ContentProvider {
private static final String TAG = "RecentsProvider";
- // TODO: offer view of recents that handles backend root resolution before
- // returning cursor, include extra columns
+ public static final long MAX_HISTORY_IN_MILLIS = DateUtils.DAY_IN_MILLIS * 45;
- public static final String AUTHORITY = "com.android.documentsui.recents";
+ private static final String AUTHORITY = "com.android.documentsui.recents";
private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- private static final int URI_RECENT_OPEN = 1;
- private static final int URI_RECENT_CREATE = 2;
+ private static final int URI_RECENT = 1;
+ private static final int URI_STATE = 2;
private static final int URI_RESUME = 3;
static {
- sMatcher.addURI(AUTHORITY, "recent_open", URI_RECENT_OPEN);
- sMatcher.addURI(AUTHORITY, "recent_create", URI_RECENT_CREATE);
+ sMatcher.addURI(AUTHORITY, "recent", URI_RECENT);
+ // state/authority/rootId/docId
+ sMatcher.addURI(AUTHORITY, "state/*/*/*", URI_STATE);
+ // resume/packageName
sMatcher.addURI(AUTHORITY, "resume/*", URI_RESUME);
}
- private static final String TABLE_RECENT_OPEN = "recent_open";
- private static final String TABLE_RECENT_CREATE = "recent_create";
- private static final String TABLE_RESUME = "resume";
-
- /**
- * String of URIs pointing at a storage backend, stored as a JSON array,
- * starting with root.
- */
- public static final String COL_PATH = "path";
- public static final String COL_URI = "uri";
- public static final String COL_PACKAGE_NAME = "package_name";
- public static final String COL_TIMESTAMP = "timestamp";
-
- @Deprecated
- public static Uri buildRecentOpen() {
- return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(AUTHORITY).appendPath("recent_open").build();
+ public static final String TABLE_RECENT = "recent";
+ public static final String TABLE_STATE = "state";
+ public static final String TABLE_RESUME = "resume";
+
+ public static class RecentColumns {
+ public static final String STACK = "stack";
+ public static final String TIMESTAMP = "timestamp";
+ }
+
+ public static class StateColumns {
+ public static final String AUTHORITY = "authority";
+ public static final String ROOT_ID = Root.COLUMN_ROOT_ID;
+ public static final String DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID;
+ public static final String MODE = "mode";
+ public static final String SORT_ORDER = "sortOrder";
}
- public static Uri buildRecentCreate() {
+ public static class ResumeColumns {
+ public static final String PACKAGE_NAME = "package_name";
+ public static final String STACK = "stack";
+ public static final String TIMESTAMP = "timestamp";
+ }
+
+ public static Uri buildRecent() {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(AUTHORITY).appendPath("recent_create").build();
+ .authority(AUTHORITY).appendPath("recent").build();
+ }
+
+ public static Uri buildState(String authority, String rootId, String documentId) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath("state").appendPath(authority).appendPath(rootId).appendPath(documentId)
+ .build();
}
public static Uri buildResume(String packageName) {
@@ -83,35 +96,42 @@ public class RecentsProvider extends ContentProvider {
private static final String DB_NAME = "recents.db";
private static final int VERSION_INIT = 1;
+ private static final int VERSION_AS_BLOB = 3;
public DatabaseHelper(Context context) {
- super(context, DB_NAME, null, VERSION_INIT);
+ super(context, DB_NAME, null, VERSION_AS_BLOB);
}
@Override
public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + TABLE_RECENT_OPEN + " (" +
- COL_URI + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
- COL_TIMESTAMP + " INTEGER" +
+
+ db.execSQL("CREATE TABLE " + TABLE_RECENT + " (" +
+ RecentColumns.STACK + " BLOB PRIMARY KEY ON CONFLICT REPLACE," +
+ RecentColumns.TIMESTAMP + " INTEGER" +
")");
- db.execSQL("CREATE TABLE " + TABLE_RECENT_CREATE + " (" +
- COL_PATH + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
- COL_TIMESTAMP + " INTEGER" +
+ db.execSQL("CREATE TABLE " + TABLE_STATE + " (" +
+ StateColumns.AUTHORITY + " TEXT," +
+ StateColumns.ROOT_ID + " TEXT," +
+ StateColumns.DOCUMENT_ID + " TEXT," +
+ StateColumns.MODE + " INTEGER," +
+ StateColumns.SORT_ORDER + " INTEGER," +
+ "PRIMARY KEY (" + StateColumns.AUTHORITY + ", " + StateColumns.ROOT_ID + ", "
+ + StateColumns.DOCUMENT_ID + ")" +
")");
db.execSQL("CREATE TABLE " + TABLE_RESUME + " (" +
- COL_PACKAGE_NAME + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
- COL_PATH + " TEXT," +
- COL_TIMESTAMP + " INTEGER" +
+ ResumeColumns.PACKAGE_NAME + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
+ ResumeColumns.STACK + " BLOB," +
+ ResumeColumns.TIMESTAMP + " INTEGER" +
")");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database; wiping app data");
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_RECENT_OPEN);
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_RECENT_CREATE);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_RECENT);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_STATE);
db.execSQL("DROP TABLE IF EXISTS " + TABLE_RESUME);
onCreate(db);
}
@@ -128,22 +148,23 @@ public class RecentsProvider extends ContentProvider {
String sortOrder) {
final SQLiteDatabase db = mHelper.getReadableDatabase();
switch (sMatcher.match(uri)) {
- case URI_RECENT_OPEN: {
- return db.query(TABLE_RECENT_OPEN, projection,
- buildWhereYounger(DateUtils.WEEK_IN_MILLIS), null, null, null, null);
- }
- case URI_RECENT_CREATE: {
- return db.query(TABLE_RECENT_CREATE, projection,
- buildWhereYounger(DateUtils.WEEK_IN_MILLIS), null, null, null, null);
- }
- case URI_RESUME: {
+ case URI_RECENT:
+ final long cutoff = System.currentTimeMillis() - MAX_HISTORY_IN_MILLIS;
+ return db.query(TABLE_RECENT, projection, RecentColumns.TIMESTAMP + ">" + cutoff,
+ null, null, null, null);
+ case URI_STATE:
+ final String authority = uri.getPathSegments().get(1);
+ final String rootId = uri.getPathSegments().get(2);
+ final String documentId = uri.getPathSegments().get(3);
+ return db.query(TABLE_STATE, projection, StateColumns.AUTHORITY + "=? AND "
+ + StateColumns.ROOT_ID + "=? AND " + StateColumns.DOCUMENT_ID + "=?",
+ new String[] { authority, rootId, documentId }, null, null, null);
+ case URI_RESUME:
final String packageName = uri.getPathSegments().get(1);
- return db.query(TABLE_RESUME, projection, COL_PACKAGE_NAME + "=?",
+ return db.query(TABLE_RESUME, projection, ResumeColumns.PACKAGE_NAME + "=?",
new String[] { packageName }, null, null, null);
- }
- default: {
+ default:
throw new UnsupportedOperationException("Unsupported Uri " + uri);
- }
}
}
@@ -156,28 +177,37 @@ public class RecentsProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mHelper.getWritableDatabase();
switch (sMatcher.match(uri)) {
- case URI_RECENT_OPEN: {
- values.put(COL_TIMESTAMP, System.currentTimeMillis());
- db.insert(TABLE_RECENT_OPEN, null, values);
- db.delete(TABLE_RECENT_OPEN, buildWhereOlder(DateUtils.WEEK_IN_MILLIS), null);
+ case URI_RECENT:
+ values.put(RecentColumns.TIMESTAMP, System.currentTimeMillis());
+ db.insert(TABLE_RECENT, null, values);
+ final long cutoff = System.currentTimeMillis() - MAX_HISTORY_IN_MILLIS;
+ db.delete(TABLE_RECENT, RecentColumns.TIMESTAMP + "<" + cutoff, null);
return uri;
- }
- case URI_RECENT_CREATE: {
- values.put(COL_TIMESTAMP, System.currentTimeMillis());
- db.insert(TABLE_RECENT_CREATE, null, values);
- db.delete(TABLE_RECENT_CREATE, buildWhereOlder(DateUtils.WEEK_IN_MILLIS), null);
+ case URI_STATE:
+ final String authority = uri.getPathSegments().get(1);
+ final String rootId = uri.getPathSegments().get(2);
+ final String documentId = uri.getPathSegments().get(3);
+
+ final ContentValues key = new ContentValues();
+ key.put(StateColumns.AUTHORITY, authority);
+ key.put(StateColumns.ROOT_ID, rootId);
+ key.put(StateColumns.DOCUMENT_ID, documentId);
+
+ // Ensure that row exists, then update with changed values
+ db.insertWithOnConflict(TABLE_STATE, null, key, SQLiteDatabase.CONFLICT_IGNORE);
+ db.update(TABLE_STATE, values, StateColumns.AUTHORITY + "=? AND "
+ + StateColumns.ROOT_ID + "=? AND " + StateColumns.DOCUMENT_ID + "=?",
+ new String[] { authority, rootId, documentId });
+
return uri;
- }
- case URI_RESUME: {
+ case URI_RESUME:
final String packageName = uri.getPathSegments().get(1);
- values.put(COL_PACKAGE_NAME, packageName);
- values.put(COL_TIMESTAMP, System.currentTimeMillis());
+ values.put(ResumeColumns.PACKAGE_NAME, packageName);
+ values.put(ResumeColumns.TIMESTAMP, System.currentTimeMillis());
db.insert(TABLE_RESUME, null, values);
return uri;
- }
- default: {
+ default:
throw new UnsupportedOperationException("Unsupported Uri " + uri);
- }
}
}
@@ -190,12 +220,4 @@ public class RecentsProvider extends ContentProvider {
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Unsupported Uri " + uri);
}
-
- private static String buildWhereOlder(long deltaMillis) {
- return COL_TIMESTAMP + "<" + (System.currentTimeMillis() - deltaMillis);
- }
-
- private static String buildWhereYounger(long deltaMillis) {
- return COL_TIMESTAMP + ">" + (System.currentTimeMillis() - deltaMillis);
- }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index d4f1b39..adf4701 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -90,7 +90,6 @@ public class RootsCache {
if (info.metaData != null && info.metaData.containsKey(
DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) {
- // TODO: remove deprecated customRoots flag
// TODO: populate roots on background thread, and cache results
final Uri rootsUri = DocumentsContract.buildRootsUri(info.authority);
final ContentProviderClient client = resolver
@@ -169,8 +168,9 @@ public class RootsCache {
if (state.localOnly && !localOnly) continue;
// Only include roots that serve requested content
- final boolean overlap = MimePredicate.mimeMatches(root.mimeTypes, state.acceptMimes)
- || MimePredicate.mimeMatches(state.acceptMimes, root.mimeTypes);
+ final boolean overlap =
+ MimePredicate.mimeMatches(root.derivedMimeTypes, state.acceptMimes) ||
+ MimePredicate.mimeMatches(state.acceptMimes, root.derivedMimeTypes);
if (!overlap) {
continue;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index a1489a5..c69103e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -20,6 +20,8 @@ import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
@@ -32,15 +34,16 @@ 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 implements Durable {
+public class DocumentInfo implements Durable, Parcelable {
private static final int VERSION_INIT = 1;
+ private static final int VERSION_SPLIT_URI = 2;
- public Uri uri;
+ public String authority;
+ public String documentId;
public String mimeType;
public String displayName;
public long lastModified;
@@ -49,13 +52,17 @@ public class DocumentInfo implements Durable {
public long size;
public int icon;
+ /** Derived fields that aren't persisted */
+ public Uri derivedUri;
+
public DocumentInfo() {
reset();
}
@Override
public void reset() {
- uri = null;
+ authority = null;
+ documentId = null;
mimeType = null;
displayName = null;
lastModified = -1;
@@ -63,6 +70,8 @@ public class DocumentInfo implements Durable {
summary = null;
size = -1;
icon = 0;
+
+ derivedUri = null;
}
@Override
@@ -70,8 +79,10 @@ public class DocumentInfo implements Durable {
final int version = in.readInt();
switch (version) {
case VERSION_INIT:
- final String rawUri = DurableUtils.readNullableString(in);
- uri = rawUri != null ? Uri.parse(rawUri) : null;
+ throw new ProtocolException("Ignored upgrade");
+ case VERSION_SPLIT_URI:
+ authority = DurableUtils.readNullableString(in);
+ documentId = DurableUtils.readNullableString(in);
mimeType = DurableUtils.readNullableString(in);
displayName = DurableUtils.readNullableString(in);
lastModified = in.readLong();
@@ -79,6 +90,7 @@ public class DocumentInfo implements Durable {
summary = DurableUtils.readNullableString(in);
size = in.readLong();
icon = in.readInt();
+ deriveFields();
break;
default:
throw new ProtocolException("Unknown version " + version);
@@ -87,8 +99,9 @@ public class DocumentInfo implements Durable {
@Override
public void write(DataOutputStream out) throws IOException {
- out.writeInt(VERSION_INIT);
- DurableUtils.writeNullableString(out, uri.toString());
+ out.writeInt(VERSION_SPLIT_URI);
+ DurableUtils.writeNullableString(out, authority);
+ DurableUtils.writeNullableString(out, documentId);
DurableUtils.writeNullableString(out, mimeType);
DurableUtils.writeNullableString(out, displayName);
out.writeLong(lastModified);
@@ -98,11 +111,41 @@ public class DocumentInfo implements Durable {
out.writeInt(icon);
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ DurableUtils.writeToParcel(dest, this);
+ }
+
+ public static final Creator<DocumentInfo> CREATOR = new Creator<DocumentInfo>() {
+ @Override
+ public DocumentInfo createFromParcel(Parcel in) {
+ final DocumentInfo doc = new DocumentInfo();
+ DurableUtils.readFromParcel(in, doc);
+ return doc;
+ }
+
+ @Override
+ public DocumentInfo[] newArray(int size) {
+ return new DocumentInfo[size];
+ }
+ };
+
public static DocumentInfo fromDirectoryCursor(Cursor cursor) {
- final DocumentInfo doc = new DocumentInfo();
final String authority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
- final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
- doc.uri = DocumentsContract.buildDocumentUri(authority, docId);
+ return fromCursor(cursor, authority);
+ }
+
+ public static DocumentInfo fromCursor(Cursor cursor, String authority) {
+ final DocumentInfo doc = new DocumentInfo();
+ doc.authority = authority;
+ doc.documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
+ doc.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ doc.documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
doc.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
doc.displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
doc.lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
@@ -110,6 +153,7 @@ public class DocumentInfo implements Durable {
doc.summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
doc.size = getCursorLong(cursor, Document.COLUMN_SIZE);
doc.icon = getCursorInt(cursor, Document.COLUMN_ICON);
+ doc.deriveFields();
return doc;
}
@@ -122,16 +166,7 @@ public class DocumentInfo implements Durable {
if (!cursor.moveToFirst()) {
throw new FileNotFoundException("Missing details for " + uri);
}
- final DocumentInfo doc = new DocumentInfo();
- doc.uri = uri;
- doc.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- doc.displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
- doc.lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
- doc.flags = getCursorInt(cursor, Document.COLUMN_FLAGS);
- doc.summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
- doc.size = getCursorLong(cursor, Document.COLUMN_SIZE);
- doc.icon = getCursorInt(cursor, Document.COLUMN_ICON);
- return doc;
+ return fromCursor(cursor, uri.getAuthority());
} catch (Throwable t) {
throw asFileNotFoundException(t);
} finally {
@@ -140,9 +175,13 @@ public class DocumentInfo implements Durable {
}
}
+ private void deriveFields() {
+ derivedUri = DocumentsContract.buildDocumentUri(authority, documentId);
+ }
+
@Override
public String toString() {
- return "Document{name=" + displayName + ", uri=" + uri + "}";
+ return "Document{name=" + displayName + ", docId=" + documentId + "}";
}
public boolean isCreateSupported() {
@@ -189,42 +228,14 @@ public class DocumentInfo implements Durable {
}
}
+ /**
+ * Missing or null values are returned as 0.
+ */
public static int getCursorInt(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
return (index != -1) ? cursor.getInt(index) : 0;
}
- @Deprecated
- public static class DisplayNameComparator implements Comparator<DocumentInfo> {
- @Override
- public int compare(DocumentInfo lhs, DocumentInfo rhs) {
- final boolean leftDir = lhs.isDirectory();
- final boolean rightDir = rhs.isDirectory();
-
- if (leftDir != rightDir) {
- return leftDir ? -1 : 1;
- } else {
- return compareToIgnoreCaseNullable(lhs.displayName, rhs.displayName);
- }
- }
- }
-
- @Deprecated
- public static class LastModifiedComparator implements Comparator<DocumentInfo> {
- @Override
- public int compare(DocumentInfo lhs, DocumentInfo rhs) {
- return Long.compare(rhs.lastModified, lhs.lastModified);
- }
- }
-
- @Deprecated
- public static class SizeComparator implements Comparator<DocumentInfo> {
- @Override
- public int compare(DocumentInfo lhs, DocumentInfo rhs) {
- return Long.compare(rhs.size, lhs.size);
- }
- }
-
public static FileNotFoundException asFileNotFoundException(Throwable t)
throws FileNotFoundException {
if (t instanceof FileNotFoundException) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
index 33a1376..2541440 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
@@ -16,8 +16,6 @@
package com.android.documentsui.model;
-import com.android.documentsui.RootsCache;
-
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
@@ -30,14 +28,13 @@ import java.util.LinkedList;
*/
public class DocumentStack extends LinkedList<DocumentInfo> implements Durable {
private static final int VERSION_INIT = 1;
+ private static final int VERSION_ADD_ROOT = 2;
- public RootInfo getRoot(RootsCache roots) {
- return roots.findRoot(getLast().uri);
- }
+ public RootInfo root;
- public String getTitle(RootsCache roots) {
- if (size() == 1) {
- return getRoot(roots).title;
+ public String getTitle() {
+ if (size() == 1 && root != null) {
+ return root.title;
} else if (size() > 1) {
return peek().displayName;
} else {
@@ -52,6 +49,7 @@ public class DocumentStack extends LinkedList<DocumentInfo> implements Durable {
@Override
public void reset() {
clear();
+ root = null;
}
@Override
@@ -59,6 +57,12 @@ public class DocumentStack extends LinkedList<DocumentInfo> implements Durable {
final int version = in.readInt();
switch (version) {
case VERSION_INIT:
+ throw new ProtocolException("Ignored upgrade");
+ case VERSION_ADD_ROOT:
+ if (in.readBoolean()) {
+ root = new RootInfo();
+ root.read(in);
+ }
final int size = in.readInt();
for (int i = 0; i < size; i++) {
final DocumentInfo doc = new DocumentInfo();
@@ -73,7 +77,13 @@ public class DocumentStack extends LinkedList<DocumentInfo> implements Durable {
@Override
public void write(DataOutputStream out) throws IOException {
- out.writeInt(VERSION_INIT);
+ out.writeInt(VERSION_ADD_ROOT);
+ if (root != null) {
+ out.writeBoolean(true);
+ root.write(out);
+ } else {
+ out.writeBoolean(false);
+ }
final int size = size();
out.writeInt(size);
for (int i = 0; i < size; i++) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index a6ddf70..e0e8acf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -23,28 +23,121 @@ import static com.android.documentsui.model.DocumentInfo.getCursorString;
import android.content.Context;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.provider.DocumentsContract.Root;
import com.android.documentsui.IconUtils;
import com.android.documentsui.R;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
import java.util.Objects;
/**
* Representation of a {@link Root}.
*/
-public class RootInfo {
+public class RootInfo implements Durable, Parcelable {
+ private static final int VERSION_INIT = 1;
+
public String authority;
public String rootId;
public int rootType;
public int flags;
public int icon;
- public int localIcon;
public String title;
public String summary;
public String documentId;
public long availableBytes;
- public String[] mimeTypes;
+ public String mimeTypes;
+
+ /** Derived fields that aren't persisted */
+ public String[] derivedMimeTypes;
+ public int derivedIcon;
+
+ public RootInfo() {
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ authority = null;
+ rootId = null;
+ rootType = 0;
+ flags = 0;
+ icon = 0;
+ title = null;
+ summary = null;
+ documentId = null;
+ availableBytes = -1;
+ mimeTypes = null;
+
+ derivedMimeTypes = null;
+ derivedIcon = 0;
+ }
+
+ @Override
+ public void read(DataInputStream in) throws IOException {
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_INIT:
+ authority = DurableUtils.readNullableString(in);
+ rootId = DurableUtils.readNullableString(in);
+ rootType = in.readInt();
+ flags = in.readInt();
+ icon = in.readInt();
+ title = DurableUtils.readNullableString(in);
+ summary = DurableUtils.readNullableString(in);
+ documentId = DurableUtils.readNullableString(in);
+ availableBytes = in.readLong();
+ mimeTypes = DurableUtils.readNullableString(in);
+ deriveFields();
+ break;
+ default:
+ throw new ProtocolException("Unknown version " + version);
+ }
+ }
+
+ @Override
+ public void write(DataOutputStream out) throws IOException {
+ out.writeInt(VERSION_INIT);
+ DurableUtils.writeNullableString(out, authority);
+ DurableUtils.writeNullableString(out, rootId);
+ out.writeInt(rootType);
+ out.writeInt(flags);
+ out.writeInt(icon);
+ DurableUtils.writeNullableString(out, title);
+ DurableUtils.writeNullableString(out, summary);
+ DurableUtils.writeNullableString(out, documentId);
+ out.writeLong(availableBytes);
+ DurableUtils.writeNullableString(out, mimeTypes);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ DurableUtils.writeToParcel(dest, this);
+ }
+
+ public static final Creator<RootInfo> CREATOR = new Creator<RootInfo>() {
+ @Override
+ public RootInfo createFromParcel(Parcel in) {
+ final RootInfo root = new RootInfo();
+ DurableUtils.readFromParcel(in, root);
+ return root;
+ }
+
+ @Override
+ public RootInfo[] newArray(int size) {
+ return new RootInfo[size];
+ }
+ };
public static RootInfo fromRootsCursor(String authority, Cursor cursor) {
final RootInfo root = new RootInfo();
@@ -57,31 +150,38 @@ public class RootInfo {
root.summary = getCursorString(cursor, Root.COLUMN_SUMMARY);
root.documentId = getCursorString(cursor, Root.COLUMN_DOCUMENT_ID);
root.availableBytes = getCursorLong(cursor, Root.COLUMN_AVAILABLE_BYTES);
+ root.mimeTypes = getCursorString(cursor, Root.COLUMN_MIME_TYPES);
+ root.deriveFields();
+ return root;
+ }
- final String raw = getCursorString(cursor, Root.COLUMN_MIME_TYPES);
- root.mimeTypes = (raw != null) ? raw.split("\n") : null;
+ private void deriveFields() {
+ derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;
// TODO: remove these special case icons
if ("com.android.externalstorage.documents".equals(authority)) {
- root.localIcon = R.drawable.ic_root_sdcard;
+ derivedIcon = R.drawable.ic_root_sdcard;
}
if ("com.android.providers.downloads.documents".equals(authority)) {
- root.localIcon = R.drawable.ic_root_download;
+ derivedIcon = R.drawable.ic_root_download;
}
if ("com.android.providers.media.documents".equals(authority)) {
- if ("image".equals(root.rootId)) {
- root.localIcon = R.drawable.ic_doc_image;
- } else if ("audio".equals(root.rootId)) {
- root.localIcon = R.drawable.ic_doc_audio;
+ if ("image".equals(rootId)) {
+ derivedIcon = R.drawable.ic_doc_image;
+ } else if ("audio".equals(rootId)) {
+ derivedIcon = R.drawable.ic_doc_audio;
}
}
+ }
- return root;
+ @Override
+ public String toString() {
+ return "Root{title=" + title + ", rootId=" + rootId + "}";
}
public Drawable loadIcon(Context context) {
- if (localIcon != 0) {
- return context.getResources().getDrawable(localIcon);
+ if (derivedIcon != 0) {
+ return context.getResources().getDrawable(derivedIcon);
} else {
return IconUtils.loadPackageIcon(context, authority, icon);
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
index f53e60d..cdb6b33 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
@@ -30,7 +30,7 @@ public class RootsCacheTest extends AndroidTestCase {
private static RootInfo buildForMimeTypes(String... mimeTypes) {
final RootInfo root = new RootInfo();
- root.mimeTypes = mimeTypes;
+ root.derivedMimeTypes = mimeTypes;
return root;
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 226d635..2326ec2 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -181,12 +181,12 @@ public class ExternalStorageProvider extends DocumentsProvider {
}
final RowBuilder row = result.newRow();
- row.offer(Document.COLUMN_DOCUMENT_ID, docId);
- row.offer(Document.COLUMN_DISPLAY_NAME, displayName);
- row.offer(Document.COLUMN_SIZE, file.length());
- row.offer(Document.COLUMN_MIME_TYPE, mimeType);
- row.offer(Document.COLUMN_LAST_MODIFIED, file.lastModified());
- row.offer(Document.COLUMN_FLAGS, flags);
+ row.add(Document.COLUMN_DOCUMENT_ID, docId);
+ row.add(Document.COLUMN_DISPLAY_NAME, displayName);
+ row.add(Document.COLUMN_SIZE, file.length());
+ row.add(Document.COLUMN_MIME_TYPE, mimeType);
+ row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
+ row.add(Document.COLUMN_FLAGS, flags);
}
@Override
@@ -197,13 +197,13 @@ public class ExternalStorageProvider extends DocumentsProvider {
final File path = mIdToPath.get(rootId);
final RowBuilder row = result.newRow();
- row.offer(Root.COLUMN_ROOT_ID, root.rootId);
- row.offer(Root.COLUMN_ROOT_TYPE, root.rootType);
- row.offer(Root.COLUMN_FLAGS, root.flags);
- row.offer(Root.COLUMN_ICON, root.icon);
- row.offer(Root.COLUMN_TITLE, root.title);
- row.offer(Root.COLUMN_DOCUMENT_ID, root.docId);
- row.offer(Root.COLUMN_AVAILABLE_BYTES, path.getFreeSpace());
+ row.add(Root.COLUMN_ROOT_ID, root.rootId);
+ row.add(Root.COLUMN_ROOT_TYPE, root.rootType);
+ row.add(Root.COLUMN_FLAGS, root.flags);
+ row.add(Root.COLUMN_ICON, root.icon);
+ row.add(Root.COLUMN_TITLE, root.title);
+ row.add(Root.COLUMN_DOCUMENT_ID, root.docId);
+ row.add(Root.COLUMN_AVAILABLE_BYTES, path.getFreeSpace());
}
return result;
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
index 872974f..014c664 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
@@ -69,13 +69,13 @@ public class TestDocumentsProvider extends DocumentsProvider {
final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
final RowBuilder row = result.newRow();
- row.offer(Root.COLUMN_ROOT_ID, MY_ROOT_ID);
- row.offer(Root.COLUMN_ROOT_TYPE, Root.ROOT_TYPE_SERVICE);
- row.offer(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS);
- row.offer(Root.COLUMN_TITLE, "_Test title which is really long");
- row.offer(Root.COLUMN_SUMMARY, "_Summary which is also super long text");
- row.offer(Root.COLUMN_DOCUMENT_ID, MY_DOC_ID);
- row.offer(Root.COLUMN_AVAILABLE_BYTES, 1024);
+ row.add(Root.COLUMN_ROOT_ID, MY_ROOT_ID);
+ row.add(Root.COLUMN_ROOT_TYPE, Root.ROOT_TYPE_SERVICE);
+ row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS);
+ row.add(Root.COLUMN_TITLE, "_Test title which is really long");
+ row.add(Root.COLUMN_SUMMARY, "_Summary which is also super long text");
+ row.add(Root.COLUMN_DOCUMENT_ID, MY_DOC_ID);
+ row.add(Root.COLUMN_AVAILABLE_BYTES, 1024);
return result;
}
@@ -125,6 +125,9 @@ public class TestDocumentsProvider extends DocumentsProvider {
includeFile(result, "_networkfile1");
includeFile(result, "_networkfile2");
includeFile(result, "_networkfile3");
+ includeFile(result, "_networkfile4");
+ includeFile(result, "_networkfile5");
+ includeFile(result, "_networkfile6");
return true;
} else {
return false;
@@ -162,6 +165,8 @@ public class TestDocumentsProvider extends DocumentsProvider {
includeFile(result, MY_DOC_NULL);
includeFile(result, "localfile1");
includeFile(result, "localfile2");
+ includeFile(result, "localfile3");
+ includeFile(result, "localfile4");
synchronized (this) {
// Try picking up an existing network fetch
@@ -229,16 +234,16 @@ public class TestDocumentsProvider extends DocumentsProvider {
private static void includeFile(MatrixCursor result, String docId) {
final RowBuilder row = result.newRow();
- row.offer(Document.COLUMN_DOCUMENT_ID, docId);
- row.offer(Document.COLUMN_DISPLAY_NAME, docId);
- row.offer(Document.COLUMN_LAST_MODIFIED, System.currentTimeMillis());
+ row.add(Document.COLUMN_DOCUMENT_ID, docId);
+ row.add(Document.COLUMN_DISPLAY_NAME, docId);
+ row.add(Document.COLUMN_LAST_MODIFIED, System.currentTimeMillis());
if (MY_DOC_ID.equals(docId)) {
- row.offer(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
} else if (MY_DOC_NULL.equals(docId)) {
// No MIME type
} else {
- row.offer(Document.COLUMN_MIME_TYPE, "application/octet-stream");
+ row.add(Document.COLUMN_MIME_TYPE, "application/octet-stream");
}
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 78b842c..40a1af6 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -50,6 +50,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.speech.hotword.HotwordRecognitionListener;
+import android.speech.hotword.HotwordRecognitionService;
import android.speech.hotword.HotwordRecognizer;
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
@@ -1775,11 +1776,13 @@ public class KeyguardHostView extends KeyguardViewBase {
public void onHotwordEvent(int eventType, Bundle eventBundle) {
if (DEBUG) Log.d(TAG, "onHotwordEvent: " + eventType);
- if (eventType == HotwordRecognizer.EVENT_TYPE_STATE_CHANGED) {
- if (eventBundle != null && eventBundle.containsKey(HotwordRecognizer.PROMPT_TEXT)) {
- new KeyguardMessageArea.Helper(
- (View) getSecurityView(mCurrentSecuritySelection))
- .setMessage(eventBundle.getString(HotwordRecognizer.PROMPT_TEXT),true);
+ if (eventType == HotwordRecognitionService.EVENT_TYPE_PROMPT_CHANGED) {
+ if (eventBundle != null
+ && eventBundle.containsKey(HotwordRecognitionService.KEY_PROMPT_TEXT)) {
+ new KeyguardMessageArea
+ .Helper((View) getSecurityView(mCurrentSecuritySelection))
+ .setMessage(eventBundle.getString(
+ HotwordRecognitionService.KEY_PROMPT_TEXT),true);
}
}
}
diff --git a/packages/SystemUI/res/drawable-nodpi/lightning.png b/packages/SystemUI/res/drawable-nodpi/lightning.png
deleted file mode 100644
index 29de308..0000000
--- a/packages/SystemUI/res/drawable-nodpi/lightning.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/values/arrays.xml b/packages/SystemUI/res/values/arrays.xml
index 0812e80..b2c8aee 100644
--- a/packages/SystemUI/res/values/arrays.xml
+++ b/packages/SystemUI/res/values/arrays.xml
@@ -51,5 +51,14 @@
<item>#FFFF3300</item>
<item>#FFFFFFFF</item>
</array>
-
+ <array name="batterymeter_bolt_points">
+ <item>88</item> <item>0</item>
+ <item>459</item><item>1</item>
+ <item>238</item><item>333</item>
+ <item>525</item><item>310</item>
+ <item>120</item><item>840</item>
+ <item>82</item> <item>818</item>
+ <item>246</item><item>373</item>
+ <item>0</item> <item>408</item>
+ </array>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index be5c326..2257617 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -23,12 +23,13 @@ import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
-import android.graphics.LightingColorFilter;
import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
import android.os.BatteryManager;
import android.os.Bundle;
import android.provider.Settings;
@@ -43,26 +44,27 @@ public class BatteryMeterView extends View implements DemoMode {
public static final boolean SINGLE_DIGIT_PERCENT = false;
public static final boolean SHOW_100_PERCENT = false;
- private static final LightingColorFilter LIGHTNING_FILTER_OPAQUE =
- new LightingColorFilter(0x00000000, 0x00000000);
- private static final LightingColorFilter LIGHTNING_FILTER_TRANS =
- new LightingColorFilter(0x00999999, 0x00000000);
-
public static final int FULL = 96;
public static final int EMPTY = 4;
int[] mColors;
boolean mShowPercent = true;
- Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint;
+ Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
int mButtonHeight;
private float mTextHeight, mWarningTextHeight;
- Drawable mLightning;
private int mHeight;
private int mWidth;
private String mWarningString;
private final int mChargeColor;
+ private final float[] mBoltPoints;
+ private final Path mBoltPath = new Path();
+
+ private final RectF mFrame = new RectF();
+ private final RectF mButtonFrame = new RectF();
+ private final RectF mClipFrame = new RectF();
+ private final Rect mBoltFrame = new Rect();
private class BatteryTracker extends BroadcastReceiver {
// current battery status
@@ -175,7 +177,8 @@ public class BatteryMeterView extends View implements DemoMode {
mColors[2*i] = levels.getInt(i, 0);
mColors[2*i+1] = colors.getColor(i, 0);
}
-
+ levels.recycle();
+ colors.recycle();
mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(
context.getContentResolver(), "status_bar_show_battery_percent", 0);
@@ -198,8 +201,28 @@ public class BatteryMeterView extends View implements DemoMode {
mWarningTextPaint.setTypeface(font);
mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
- mLightning = getResources().getDrawable(R.drawable.lightning);
mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);
+
+ mBoltPaint = new Paint();
+ mBoltPaint.setAntiAlias(true);
+ mBoltPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); // punch hole
+ setLayerType(LAYER_TYPE_HARDWARE, null);
+ mBoltPoints = loadBoltPoints(res);
+ }
+
+ private static float[] loadBoltPoints(Resources res) {
+ final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);
+ int maxX = 0, maxY = 0;
+ for (int i = 0; i < pts.length; i += 2) {
+ maxX = Math.max(maxX, pts[i]);
+ maxY = Math.max(maxY, pts[i + 1]);
+ }
+ final float[] ptsF = new float[pts.length];
+ for (int i = 0; i < pts.length; i += 2) {
+ ptsF[i] = (float)pts[i] / maxX;
+ ptsF[i + 1] = (float)pts[i + 1] / maxY;
+ }
+ return ptsF;
}
@Override
@@ -220,15 +243,6 @@ public class BatteryMeterView extends View implements DemoMode {
return color;
}
- // TODO jspurlock - remove once we draw hollow bolt in code
- public void setBarTransparent(boolean isTransparent) {
- mLightning.setColorFilter(isTransparent ? LIGHTNING_FILTER_TRANS : LIGHTNING_FILTER_OPAQUE);
- BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;
- if (tracker.plugged) {
- postInvalidate();
- }
- }
-
@Override
public void draw(Canvas c) {
BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;
@@ -243,22 +257,19 @@ public class BatteryMeterView extends View implements DemoMode {
mButtonHeight = (int) (height * 0.12f);
- final RectF frame = new RectF(0, 0, width, height);
- frame.offset(pl, pt);
-
- // Log.v("BatteryGauge", String.format("canvas: %dx%d frame: %s",
- // c.getWidth(), c.getHeight(), frame.toString()));
+ mFrame.set(0, 0, width, height);
+ mFrame.offset(pl, pt);
- final RectF buttonframe = new RectF(
- frame.left + width * 0.25f,
- frame.top,
- frame.right - width * 0.25f,
- frame.top + mButtonHeight);
+ mButtonFrame.set(
+ mFrame.left + width * 0.25f,
+ mFrame.top,
+ mFrame.right - width * 0.25f,
+ mFrame.top + mButtonHeight);
- frame.top += mButtonHeight;
+ mFrame.top += mButtonHeight;
// first, draw the battery shape
- c.drawRect(frame, mFramePaint);
+ c.drawRect(mFrame, mFramePaint);
// fill 'er up
final int pct = tracker.level;
@@ -271,15 +282,14 @@ public class BatteryMeterView extends View implements DemoMode {
drawFrac = 0f;
}
- c.drawRect(buttonframe,
- drawFrac == 1f ? mBatteryPaint : mFramePaint);
+ c.drawRect(mButtonFrame, drawFrac == 1f ? mBatteryPaint : mFramePaint);
- RectF clip = new RectF(frame);
- clip.top += (frame.height() * (1f - drawFrac));
+ mClipFrame.set(mFrame);
+ mClipFrame.top += (mFrame.height() * (1f - drawFrac));
c.save(Canvas.CLIP_SAVE_FLAG);
- c.clipRect(clip);
- c.drawRect(frame, mBatteryPaint);
+ c.clipRect(mClipFrame);
+ c.drawRect(mFrame, mBatteryPaint);
c.restore();
if (level <= EMPTY) {
@@ -287,11 +297,28 @@ public class BatteryMeterView extends View implements DemoMode {
final float y = (mHeight + mWarningTextHeight) * 0.48f;
c.drawText(mWarningString, x, y, mWarningTextPaint);
} else if (tracker.plugged) {
- final Rect r = new Rect(
- (int)frame.left + width / 4, (int)frame.top + height / 5,
- (int)frame.right - width / 4, (int)frame.bottom - height / 6);
- mLightning.setBounds(r);
- mLightning.draw(c);
+ // draw the bolt
+ final int bl = (int)(mFrame.left + width / 4f);
+ final int bt = (int)(mFrame.top + height / 6f);
+ final int br = (int)(mFrame.right - width / 5f);
+ final int bb = (int)(mFrame.bottom - height / 6f);
+ if (mBoltFrame.left != bl || mBoltFrame.top != bt
+ || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
+ mBoltFrame.set(bl, bt, br, bb);
+ mBoltPath.reset();
+ mBoltPath.moveTo(
+ mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
+ mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
+ for (int i = 2; i < mBoltPoints.length; i += 2) {
+ mBoltPath.lineTo(
+ mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
+ mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
+ }
+ mBoltPath.lineTo(
+ mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
+ mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
+ }
+ c.drawPath(mBoltPath, mBoltPaint);
} else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) {
mTextPaint.setTextSize(height *
(SINGLE_DIGIT_PERCENT ? 0.75f
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index f8b6ca6..b263a6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.ActivityManager;
import android.content.Context;
@@ -28,7 +29,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
-import com.android.systemui.BatteryMeterView;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
@@ -53,9 +53,7 @@ public class PhoneStatusBarView extends PanelBar {
private final int mTransparent;
private final float mAlphaWhenOpaque;
private final float mAlphaWhenTransparent = 1;
- private View mLeftSide;
- private View mRightSide;
- private BatteryMeterView mBattery;
+ private View mLeftSide, mStatusIcons, mSignalCluster, mClock;
public StatusBarTransitions(Context context) {
super(context, PhoneStatusBarView.this);
@@ -66,8 +64,9 @@ public class PhoneStatusBarView extends PanelBar {
public void init() {
mLeftSide = findViewById(R.id.notification_icon_area);
- mRightSide = findViewById(R.id.system_icon_area);
- mBattery = (BatteryMeterView) findViewById(R.id.battery);
+ mStatusIcons = findViewById(R.id.statusIcons);
+ mSignalCluster = findViewById(R.id.signal_battery_cluster);
+ mClock = findViewById(R.id.clock);
applyMode(getMode(), false /*animate*/);
}
@@ -96,17 +95,22 @@ public class PhoneStatusBarView extends PanelBar {
}
private void applyMode(int mode, boolean animate) {
- if (mLeftSide == null || mRightSide == null) return;
- mBattery.setBarTransparent(isTransparent(mode));
+ if (mLeftSide == null) return; // pre-init
float newAlpha = getAlphaFor(mode);
if (animate) {
- ObjectAnimator lhs = animateTransitionTo(mLeftSide, newAlpha);
- lhs.start();
- // TODO jspurlock - fix conflicting rhs animations on tablets
- mRightSide.setAlpha(newAlpha);
+ AnimatorSet anims = new AnimatorSet();
+ anims.playTogether(
+ animateTransitionTo(mLeftSide, newAlpha),
+ animateTransitionTo(mStatusIcons, newAlpha),
+ animateTransitionTo(mSignalCluster, newAlpha),
+ animateTransitionTo(mClock, newAlpha)
+ );
+ anims.start();
} else {
mLeftSide.setAlpha(newAlpha);
- mRightSide.setAlpha(newAlpha);
+ mStatusIcons.setAlpha(newAlpha);
+ mSignalCluster.setAlpha(newAlpha);
+ mClock.setAlpha(newAlpha);
}
}
}