summaryrefslogtreecommitdiffstats
path: root/packages/DocumentsUI
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-09-27 16:44:11 -0700
committerJeff Sharkey <jsharkey@android.com>2013-09-27 17:13:13 -0700
commit6efba22ce510352bb84910d6efc42fecafd31ed7 (patch)
tree0bb0df74be266330bdc5c86d686abf39bb0f914d /packages/DocumentsUI
parent3d52dc9c3a2fd9997322ce5e28607b3f7e9bfcf7 (diff)
downloadframeworks_base-6efba22ce510352bb84910d6efc42fecafd31ed7.zip
frameworks_base-6efba22ce510352bb84910d6efc42fecafd31ed7.tar.gz
frameworks_base-6efba22ce510352bb84910d6efc42fecafd31ed7.tar.bz2
New roots UX, async, performance, docs.
Yet another iteration from UX on how roots should be ordered. Since we no longer categorize by type, remove from public API. Updated asset drop with new dividers. Update public API docs to be explicit about required columns. Hide flags and columns that aren't required for third-party apps. Move remainder of potentially blocking work to AsyncTasks, including creating directories, picked root resolution, and creation of new documents once picked. Improve performance of layouts by removing baseline alignment and reduce hierarchy depth. Set alpha on ImageViews directly to avoid offscreen rendering hit. Limit returned recents to 45 days. Show load in recents when still waiting for backends. Show empty message when no recents stacks to create from. Use unique key when saving recent stacks. Bug: 10941423, 10819454, 10964412, 10960718 Change-Id: I08cf589dcda7e203acf67928f4d30322ae36ee94
Diffstat (limited to 'packages/DocumentsUI')
-rw-r--r--packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.pngbin244 -> 184 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.pngbin0 -> 119 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.pngbin0 -> 150 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.pngbin244 -> 163 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.pngbin0 -> 119 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.pngbin0 -> 132 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.pngbin244 -> 193 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.pngbin0 -> 119 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.pngbin0 -> 154 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.pngbin244 -> 233 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.pngbin0 -> 119 bytes
-rw-r--r--packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.pngbin0 -> 156 bytes
-rw-r--r--packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml15
-rw-r--r--packages/DocumentsUI/res/layout-sw720dp/activity.xml5
-rw-r--r--packages/DocumentsUI/res/layout/activity.xml2
-rw-r--r--packages/DocumentsUI/res/layout/fragment_roots.xml2
-rw-r--r--packages/DocumentsUI/res/layout/fragment_save.xml3
-rw-r--r--packages/DocumentsUI/res/layout/item_doc_grid.xml57
-rw-r--r--packages/DocumentsUI/res/layout/item_doc_list.xml38
-rw-r--r--packages/DocumentsUI/res/layout/item_loading_grid.xml4
-rw-r--r--packages/DocumentsUI/res/layout/item_loading_list.xml5
-rw-r--r--packages/DocumentsUI/res/layout/item_message_list.xml9
-rw-r--r--packages/DocumentsUI/res/layout/item_root.xml3
-rw-r--r--packages/DocumentsUI/res/layout/item_root_spacer.xml20
-rw-r--r--packages/DocumentsUI/res/layout/item_title.xml3
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java52
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java14
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java6
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java112
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java12
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java43
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java10
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java9
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsCache.java3
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java236
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/TestActivity.java22
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java19
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java63
38 files changed, 497 insertions, 270 deletions
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png
index 0240874..904d525 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..0d75172
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..403eddb
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png
index 0240874..068619b 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..0d75172
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..9a9cf5e
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png
index 0240874..e38a868 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..0d75172
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..205c34b
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png
index 0240874..0b332e4 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..32b5f98
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..f47d50a
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index 3bea166..851061f 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -21,17 +21,18 @@
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"
- android:orientation="horizontal">
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:baselineAligned="false">
<FrameLayout
android:id="@android:id/icon"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginStart="12dp"
- android:layout_marginEnd="20dp"
- android:layout_gravity="center_vertical">
+ android:layout_marginEnd="20dp">
<ImageView
android:id="@+id/icon_mime"
@@ -49,11 +50,11 @@
</FrameLayout>
+ <!-- This is the one special case where we want baseline alignment! -->
<LinearLayout
- android:layout_width="0dip"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_gravity="center_vertical"
android:orientation="horizontal">
<TextView
diff --git a/packages/DocumentsUI/res/layout-sw720dp/activity.xml b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
index 78735fd..9286277 100644
--- a/packages/DocumentsUI/res/layout-sw720dp/activity.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
@@ -17,7 +17,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:baselineAligned="false">
<FrameLayout
android:layout_width="wrap_content"
@@ -47,7 +48,7 @@
<com.android.documentsui.DirectoryContainerView
android:id="@+id/container_directory"
android:layout_width="match_parent"
- android:layout_height="0dip"
+ android:layout_height="0dp"
android:layout_weight="1" />
<FrameLayout
diff --git a/packages/DocumentsUI/res/layout/activity.xml b/packages/DocumentsUI/res/layout/activity.xml
index 9937c39..2ef7e9c 100644
--- a/packages/DocumentsUI/res/layout/activity.xml
+++ b/packages/DocumentsUI/res/layout/activity.xml
@@ -27,7 +27,7 @@
<com.android.documentsui.DirectoryContainerView
android:id="@+id/container_directory"
android:layout_width="match_parent"
- android:layout_height="0dip"
+ android:layout_height="0dp"
android:layout_weight="1" />
<FrameLayout
diff --git a/packages/DocumentsUI/res/layout/fragment_roots.xml b/packages/DocumentsUI/res/layout/fragment_roots.xml
index 09782d9..c3a3da0 100644
--- a/packages/DocumentsUI/res/layout/fragment_roots.xml
+++ b/packages/DocumentsUI/res/layout/fragment_roots.xml
@@ -18,4 +18,4 @@
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:divider="@null" />
+ android:divider="@drawable/ic_drawer_hairline_divider" />
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
index 570b517..891f0a0 100644
--- a/packages/DocumentsUI/res/layout/fragment_save.xml
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -29,6 +29,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:baselineAligned="false"
android:gravity="center_vertical"
android:background="#ddd"
android:minHeight="?android:attr/listPreferredItemHeightSmall">
@@ -44,7 +45,7 @@
<EditText
android:id="@android:id/title"
- android:layout_width="0dip"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml
index b745bb9..bb5dce1 100644
--- a/packages/DocumentsUI/res/layout/item_doc_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml
@@ -28,37 +28,25 @@
<FrameLayout
android:layout_width="match_parent"
- android:layout_height="0dip"
+ android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginBottom="6dp"
- android:background="#fff">
+ android:background="#fff"
+ android:foreground="@drawable/ic_grid_gradient_bg"
+ android:foregroundGravity="fill">
- <FrameLayout
- android:id="@android:id/icon"
+ <ImageView
+ android:id="@+id/icon_mime"
android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <ImageView
- android:id="@+id/icon_mime"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerInside"
- android:contentDescription="@null" />
-
- <ImageView
- android:id="@+id/icon_thumb"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:contentDescription="@null" />
-
- </FrameLayout>
+ android:layout_height="match_parent"
+ android:scaleType="centerInside"
+ android:contentDescription="@null" />
<ImageView
+ android:id="@+id/icon_thumb"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:scaleType="fitXY"
- android:src="@drawable/ic_grid_gradient_bg"
+ android:scaleType="centerCrop"
android:contentDescription="@null" />
</FrameLayout>
@@ -67,7 +55,9 @@
android:id="@+id/line1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="center_vertical"
android:orientation="horizontal"
+ android:baselineAligned="false"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
@@ -85,7 +75,7 @@
android:id="@android:id/icon1"
android:layout_width="@dimen/root_icon_size"
android:layout_height="@dimen/root_icon_size"
- android:layout_marginStart="8dip"
+ android:layout_marginStart="8dp"
android:scaleType="centerInside"
android:contentDescription="@null" />
@@ -95,16 +85,17 @@
android:id="@+id/line2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="center_vertical"
android:orientation="horizontal"
+ android:baselineAligned="false"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<TextView
android:id="@+id/date"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:minWidth="80dp"
+ android:layout_weight="0.5"
android:singleLine="true"
android:ellipsize="marquee"
android:textAlignment="viewStart"
@@ -112,26 +103,20 @@
<TextView
android:id="@+id/size"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_weight="0.5"
android:layout_marginStart="8dp"
- android:minWidth="80dp"
android:singleLine="true"
android:ellipsize="marquee"
android:textAlignment="viewStart"
style="@style/TextAppearance.Small" />
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1" />
-
<ImageView
android:id="@android:id/icon2"
android:layout_width="@dimen/root_icon_size"
android:layout_height="@dimen/root_icon_size"
- android:layout_marginStart="8dip"
+ android:layout_marginStart="8dp"
android:scaleType="centerInside"
android:contentDescription="@null"
android:visibility="gone" />
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index 84fda9d..4c5b2e3 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -21,17 +21,18 @@
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"
- android:orientation="horizontal">
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:baselineAligned="false">
<FrameLayout
android:id="@android:id/icon"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginStart="12dp"
- android:layout_marginEnd="20dp"
- android:layout_gravity="center_vertical">
+ android:layout_marginEnd="20dp">
<ImageView
android:id="@+id/icon_mime"
@@ -50,20 +51,20 @@
</FrameLayout>
<LinearLayout
- android:layout_width="0dip"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_gravity="center_vertical"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:baselineAligned="false">
<TextView
android:id="@android:id/title"
- android:layout_width="0dip"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
@@ -75,7 +76,7 @@
android:id="@android:id/icon1"
android:layout_width="@dimen/root_icon_size"
android:layout_height="@dimen/root_icon_size"
- android:layout_marginStart="8dip"
+ android:layout_marginStart="8dp"
android:scaleType="centerInside"
android:contentDescription="@null" />
@@ -85,13 +86,15 @@
android:id="@+id/line2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:baselineAligned="false">
<TextView
android:id="@+id/date"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_weight="0.25"
android:minWidth="70dp"
android:singleLine="true"
android:ellipsize="marquee"
@@ -100,11 +103,11 @@
<TextView
android:id="@+id/size"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:minWidth="70dp"
+ android:layout_weight="0.25"
android:layout_marginStart="8dp"
+ android:minWidth="70dp"
android:singleLine="true"
android:ellipsize="marquee"
android:textAlignment="viewStart"
@@ -114,8 +117,7 @@
android:id="@android:id/summary"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_vertical"
+ android:layout_weight="0.5"
android:layout_marginStart="8dp"
android:singleLine="true"
android:ellipsize="marquee"
diff --git a/packages/DocumentsUI/res/layout/item_loading_grid.xml b/packages/DocumentsUI/res/layout/item_loading_grid.xml
index 21be137..0bf6137 100644
--- a/packages/DocumentsUI/res/layout/item_loading_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_loading_grid.xml
@@ -20,8 +20,8 @@
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
android:orientation="horizontal">
<ProgressBar
diff --git a/packages/DocumentsUI/res/layout/item_loading_list.xml b/packages/DocumentsUI/res/layout/item_loading_list.xml
index 7da71e3..cdcd01d 100644
--- a/packages/DocumentsUI/res/layout/item_loading_list.xml
+++ b/packages/DocumentsUI/res/layout/item_loading_list.xml
@@ -20,9 +20,8 @@
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"
- android:orientation="horizontal">
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
<ProgressBar
android:layout_width="wrap_content"
diff --git a/packages/DocumentsUI/res/layout/item_message_list.xml b/packages/DocumentsUI/res/layout/item_message_list.xml
index ffda98c..2bcbc2d 100644
--- a/packages/DocumentsUI/res/layout/item_message_list.xml
+++ b/packages/DocumentsUI/res/layout/item_message_list.xml
@@ -21,15 +21,16 @@
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"
- android:orientation="horizontal">
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:orientation="horizontal"
+ android:baselineAligned="false">
<ImageView
android:id="@android:id/icon"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
- android:layout_marginEnd="8dip"
+ android:layout_marginEnd="8dp"
android:layout_gravity="center_vertical"
android:scaleType="centerInside"
android:contentDescription="@null" />
diff --git a/packages/DocumentsUI/res/layout/item_root.xml b/packages/DocumentsUI/res/layout/item_root.xml
index 98d78da..9b52d85 100644
--- a/packages/DocumentsUI/res/layout/item_root.xml
+++ b/packages/DocumentsUI/res/layout/item_root.xml
@@ -22,13 +22,14 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:gravity="center_vertical"
android:orientation="horizontal"
+ android:baselineAligned="false"
android:background="@drawable/item_root">
<ImageView
android:id="@android:id/icon"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
- android:layout_marginEnd="8dip"
+ android:layout_marginEnd="8dp"
android:scaleType="centerInside"
android:contentDescription="@null" />
diff --git a/packages/DocumentsUI/res/layout/item_root_spacer.xml b/packages/DocumentsUI/res/layout/item_root_spacer.xml
new file mode 100644
index 0000000..7d96ac8
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/item_root_spacer.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/ic_drawer_tall_divider" />
diff --git a/packages/DocumentsUI/res/layout/item_title.xml b/packages/DocumentsUI/res/layout/item_title.xml
index 7eb100a..58016f1 100644
--- a/packages/DocumentsUI/res/layout/item_title.xml
+++ b/packages/DocumentsUI/res/layout/item_title.xml
@@ -21,7 +21,8 @@
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:gravity="center_vertical"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:baselineAligned="false">
<ImageView
android:id="@+id/subdir"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 9d92cd8..48bfaf0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
@@ -35,6 +36,8 @@ import android.widget.Toast;
import com.android.documentsui.model.DocumentInfo;
+import java.io.FileNotFoundException;
+
/**
* Dialog to create a new directory.
*/
@@ -64,24 +67,45 @@ public class CreateDirectoryFragment extends DialogFragment {
@Override
public void onClick(DialogInterface dialog, int which) {
final String displayName = text1.getText().toString();
-
- final DocumentsActivity activity = (DocumentsActivity) getActivity();
- final DocumentInfo cwd = activity.getCurrentDirectory();
-
- try {
- final Uri childUri = DocumentsContract.createDocument(
- resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, displayName);
-
- // Navigate into newly created child
- final DocumentInfo childDoc = DocumentInfo.fromUri(resolver, childUri);
- activity.onDocumentPicked(childDoc);
- } catch (Exception e) {
- Toast.makeText(context, R.string.create_error, Toast.LENGTH_SHORT).show();
- }
+ new CreateDirectoryTask(displayName).execute();
}
});
builder.setNegativeButton(android.R.string.cancel, null);
return builder.create();
}
+
+ private class CreateDirectoryTask extends AsyncTask<Void, Void, DocumentInfo> {
+ private final String mDisplayName;
+
+ public CreateDirectoryTask(String displayName) {
+ mDisplayName = displayName;
+ }
+
+ @Override
+ protected DocumentInfo doInBackground(Void... params) {
+ final DocumentsActivity activity = (DocumentsActivity) getActivity();
+ final ContentResolver resolver = activity.getContentResolver();
+
+ final DocumentInfo cwd = activity.getCurrentDirectory();
+ final Uri childUri = DocumentsContract.createDocument(
+ resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName);
+ try {
+ return DocumentInfo.fromUri(resolver, childUri);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(DocumentInfo result) {
+ final DocumentsActivity activity = (DocumentsActivity) getActivity();
+ if (result != null) {
+ // Navigate into newly created child
+ activity.onDocumentPicked(result);
+ } else {
+ Toast.makeText(activity, R.string.create_error, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index c46dfb2..1f11aed 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -742,7 +742,6 @@ public class DirectoryFragment extends Fragment {
final View line1 = convertView.findViewById(R.id.line1);
final View line2 = convertView.findViewById(R.id.line2);
- final View icon = convertView.findViewById(android.R.id.icon);
final ImageView iconMime = (ImageView) convertView.findViewById(R.id.icon_mime);
final ImageView iconThumb = (ImageView) convertView.findViewById(R.id.icon_thumb);
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
@@ -786,10 +785,12 @@ public class DirectoryFragment extends Fragment {
// loaded in background.
if (cacheHit) {
iconMime.setAlpha(0f);
+ iconMime.setImageDrawable(null);
iconThumb.setAlpha(1f);
} else {
iconMime.setAlpha(1f);
iconThumb.setAlpha(0f);
+ iconThumb.setImageDrawable(null);
if (docIcon != 0) {
iconMime.setImageDrawable(
IconUtils.loadPackageIcon(context, docAuthority, docIcon));
@@ -895,12 +896,14 @@ public class DirectoryFragment extends Fragment {
final boolean enabled = isDocumentEnabled(docMimeType, docFlags);
if (enabled) {
setEnabledRecursive(convertView, true);
- icon.setAlpha(1f);
+ iconMime.setAlpha(1f);
+ iconThumb.setAlpha(1f);
if (icon1 != null) icon1.setAlpha(1f);
if (icon2 != null) icon2.setAlpha(1f);
} else {
setEnabledRecursive(convertView, false);
- icon.setAlpha(0.5f);
+ iconMime.setAlpha(0.5f);
+ iconThumb.setAlpha(0.5f);
if (icon1 != null) icon1.setAlpha(0.5f);
if (icon2 != null) icon2.setAlpha(0.5f);
}
@@ -991,10 +994,11 @@ public class DirectoryFragment extends Fragment {
mIconThumb.setTag(null);
mIconThumb.setImageBitmap(result);
- mIconMime.setAlpha(1f);
+ final float targetAlpha = mIconMime.isEnabled() ? 1f : 0.5f;
+ mIconMime.setAlpha(targetAlpha);
mIconMime.animate().alpha(0f).start();
mIconThumb.setAlpha(0f);
- mIconThumb.animate().alpha(1f).start();
+ mIconThumb.animate().alpha(targetAlpha).start();
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 8627ecf..0b3ecf8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -63,6 +63,9 @@ class DirectoryResult implements AutoCloseable {
}
public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
+
+ private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
+
private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
private final int mType;
@@ -164,8 +167,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
if (mType == DirectoryFragment.TYPE_SEARCH) {
// Filter directories out of search results, for now
- cursor = new FilteringCursorWrapper(cursor, null, new String[] {
- Document.MIME_TYPE_DIR });
+ cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
} else {
// Normal directories should have sorting applied
cursor = new SortingCursorWrapper(cursor, result.sortOrder);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 72fdc57..4caec8f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -854,14 +854,7 @@ public class DocumentsActivity extends Activity {
mState.stackTouched = true;
if (!mRoots.isRecentsRoot(root)) {
- try {
- final Uri uri = DocumentsContract.buildDocumentUri(root.authority, root.documentId);
- final DocumentInfo doc = DocumentInfo.fromUri(getContentResolver(), uri);
- mState.stack.push(doc);
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_SIDE);
- } catch (FileNotFoundException e) {
- }
+ new PickRootTask(root).execute();
} else {
onCurrentDirectoryChanged(ANIM_SIDE);
}
@@ -871,6 +864,34 @@ public class DocumentsActivity extends Activity {
}
}
+ private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
+ private RootInfo mRoot;
+
+ public PickRootTask(RootInfo root) {
+ mRoot = root;
+ }
+
+ @Override
+ protected DocumentInfo doInBackground(Void... params) {
+ try {
+ final Uri uri = DocumentsContract.buildDocumentUri(
+ mRoot.authority, mRoot.documentId);
+ return DocumentInfo.fromUri(getContentResolver(), uri);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(DocumentInfo result) {
+ if (result != null) {
+ mState.stack.push(result);
+ mState.stackTouched = true;
+ onCurrentDirectoryChanged(ANIM_SIDE);
+ }
+ }
+ }
+
public void onAppPicked(ResolveInfo info) {
final Intent intent = new Intent(getIntent());
intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
@@ -909,7 +930,7 @@ public class DocumentsActivity extends Activity {
onCurrentDirectoryChanged(ANIM_DOWN);
} else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
// Explicit file picked, return
- onFinished(doc.derivedUri);
+ new ExistingFinishTask(doc.derivedUri).execute();
} else if (mState.action == ACTION_CREATE) {
// Replace selected file
SaveFragment.get(fm).setReplaceTarget(doc);
@@ -943,29 +964,19 @@ public class DocumentsActivity extends Activity {
for (int i = 0; i < size; i++) {
uris[i] = docs.get(i).derivedUri;
}
- onFinished(uris);
+ new ExistingFinishTask(uris).execute();
}
}
public void onSaveRequested(DocumentInfo replaceTarget) {
- onFinished(replaceTarget.derivedUri);
+ new ExistingFinishTask(replaceTarget.derivedUri).execute();
}
public void onSaveRequested(String mimeType, String displayName) {
- final DocumentInfo cwd = getCurrentDirectory();
-
- final Uri childUri = DocumentsContract.createDocument(
- getContentResolver(), cwd.derivedUri, mimeType, displayName);
- if (childUri != null) {
- onFinished(childUri);
- } else {
- Toast.makeText(this, R.string.save_error, Toast.LENGTH_SHORT).show();
- }
+ new CreateFinishTask(mimeType, displayName).execute();
}
- private void onFinished(Uri... uris) {
- Log.d(TAG, "onFinished() " + Arrays.toString(uris));
-
+ private void saveStackBlocking() {
final ContentResolver resolver = getContentResolver();
final ContentValues values = new ContentValues();
@@ -973,6 +984,7 @@ public class DocumentsActivity extends Activity {
if (mState.action == ACTION_CREATE) {
// Remember stack for last create
values.clear();
+ values.put(RecentColumns.KEY, mState.stack.buildKey());
values.put(RecentColumns.STACK, rawStack);
resolver.insert(RecentsProvider.buildRecent(), values);
}
@@ -983,6 +995,10 @@ public class DocumentsActivity extends Activity {
values.put(ResumeColumns.STACK, rawStack);
values.put(ResumeColumns.EXTERNAL, 0);
resolver.insert(RecentsProvider.buildResume(packageName), values);
+ }
+
+ private void onFinished(Uri... uris) {
+ Log.d(TAG, "onFinished() " + Arrays.toString(uris));
final Intent intent = new Intent();
if (uris.length == 1) {
@@ -1008,6 +1024,56 @@ public class DocumentsActivity extends Activity {
finish();
}
+ private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
+ private final String mMimeType;
+ private final String mDisplayName;
+
+ public CreateFinishTask(String mimeType, String displayName) {
+ mMimeType = mimeType;
+ mDisplayName = displayName;
+ }
+
+ @Override
+ protected Uri doInBackground(Void... params) {
+ final DocumentInfo cwd = getCurrentDirectory();
+ final Uri childUri = DocumentsContract.createDocument(
+ getContentResolver(), cwd.derivedUri, mMimeType, mDisplayName);
+ if (childUri != null) {
+ saveStackBlocking();
+ }
+ return childUri;
+ }
+
+ @Override
+ protected void onPostExecute(Uri result) {
+ if (result != null) {
+ onFinished(result);
+ } else {
+ Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+ }
+
+ private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
+ private final Uri[] mUris;
+
+ public ExistingFinishTask(Uri... uris) {
+ mUris = uris;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ saveStackBlocking();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ onFinished(mUris);
+ }
+ }
+
public static class State implements android.os.Parcelable {
public int action;
public String[] acceptMimes;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
index 5f56963..52d816f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
@@ -34,10 +34,15 @@ public class FilteringCursorWrapper extends AbstractCursor {
private int mCount;
public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes) {
- this(cursor, acceptMimes, null);
+ this(cursor, acceptMimes, null, Long.MIN_VALUE);
}
public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes, String[] rejectMimes) {
+ this(cursor, acceptMimes, rejectMimes, Long.MIN_VALUE);
+ }
+
+ public FilteringCursorWrapper(
+ Cursor cursor, String[] acceptMimes, String[] rejectMimes, long rejectBefore) {
mCursor = cursor;
final int count = cursor.getCount();
@@ -47,9 +52,14 @@ public class FilteringCursorWrapper extends AbstractCursor {
while (cursor.moveToNext()) {
final String mimeType = cursor.getString(
cursor.getColumnIndex(Document.COLUMN_MIME_TYPE));
+ final long lastModified = cursor.getLong(
+ cursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED));
if (rejectMimes != null && MimePredicate.mimeMatches(rejectMimes, mimeType)) {
continue;
}
+ if (lastModified < rejectBefore) {
+ continue;
+ }
if (MimePredicate.mimeMatches(acceptMimes, mimeType)) {
mPosition[mCount++] = cursor.getPosition();
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index e390456..9a4fb7d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -26,9 +26,11 @@ import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.net.Uri;
+import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
+import android.text.format.DateUtils;
import android.util.Log;
import com.android.documentsui.DocumentsActivity.State;
@@ -54,17 +56,23 @@ import java.util.concurrent.TimeUnit;
public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
private static final boolean LOGD = true;
- public static final int MAX_OUTSTANDING_RECENTS = 2;
+ // TODO: adjust for svelte devices
+ // TODO: add support for oneway queries to avoid wedging loader
+ private static final int MAX_OUTSTANDING_RECENTS = 2;
/**
* Time to wait for first pass to complete before returning partial results.
*/
- public static final int MAX_FIRST_PASS_WAIT_MILLIS = 500;
+ private static final int MAX_FIRST_PASS_WAIT_MILLIS = 500;
- /**
- * Maximum documents from a single root.
- */
- public static final int MAX_DOCS_FROM_ROOT = 64;
+ /** Maximum documents from a single root. */
+ private static final int MAX_DOCS_FROM_ROOT = 64;
+
+ /** Ignore documents older than this age. */
+ private static final long REJECT_OLDER_THAN = 45 * DateUtils.DAY_IN_MILLIS;
+
+ /** MIME types that should always be excluded from recents. */
+ private static final String[] RECENT_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
private static final ExecutorService sExecutor = buildExecutor();
@@ -173,6 +181,8 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
}
}
+ final long rejectBefore = System.currentTimeMillis() - REJECT_OLDER_THAN;
+
// Collect all finished tasks
List<Cursor> cursors = Lists.newArrayList();
for (RecentTask task : mTasks.values()) {
@@ -180,7 +190,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
try {
final Cursor cursor = task.get();
final FilteringCursorWrapper filtered = new FilteringCursorWrapper(
- cursor, mState.acceptMimes, new String[] { Document.MIME_TYPE_DIR }) {
+ cursor, mState.acceptMimes, RECENT_REJECT_MIMES, rejectBefore) {
@Override
public void close() {
// Ignored, since we manage cursor lifecycle internally
@@ -203,11 +213,22 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
final DirectoryResult result = new DirectoryResult();
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, result.sortOrder);
- result.cursor = sorted;
+ // Hint to UI if we're still loading
+ final Bundle extras = new Bundle();
+ if (cursors.size() != mTasks.size()) {
+ extras.putBoolean(DocumentsContract.EXTRA_LOADING, true);
}
+
+ final MergeCursor merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
+ final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder) {
+ @Override
+ public Bundle getExtras() {
+ return extras;
+ }
+ };
+
+ 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 c975382..3954173 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -66,6 +66,7 @@ import java.util.List;
*/
public class RecentsCreateFragment extends Fragment {
+ private View mEmptyView;
private ListView mListView;
private DocumentStackAdapter mAdapter;
@@ -87,6 +88,8 @@ public class RecentsCreateFragment extends Fragment {
final View view = inflater.inflate(R.layout.fragment_directory, container, false);
+ mEmptyView = view.findViewById(android.R.id.empty);
+
mListView = (ListView) view.findViewById(R.id.list);
mListView.setOnItemClickListener(mItemListener);
@@ -189,6 +192,13 @@ public class RecentsCreateFragment extends Fragment {
public void swapStacks(List<DocumentStack> stacks) {
mStacks = stacks;
+
+ if (isEmpty()) {
+ mEmptyView.setVisibility(View.VISIBLE);
+ } else {
+ mEmptyView.setVisibility(View.GONE);
+ }
+
notifyDataSetChanged();
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index 7386cae..4313fa7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -33,7 +33,7 @@ import android.util.Log;
public class RecentsProvider extends ContentProvider {
private static final String TAG = "RecentsProvider";
- public static final long MAX_HISTORY_IN_MILLIS = DateUtils.DAY_IN_MILLIS * 45;
+ public static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
private static final String AUTHORITY = "com.android.documentsui.recents";
@@ -56,6 +56,7 @@ public class RecentsProvider extends ContentProvider {
public static final String TABLE_RESUME = "resume";
public static class RecentColumns {
+ public static final String KEY = "key";
public static final String STACK = "stack";
public static final String TIMESTAMP = "timestamp";
}
@@ -99,16 +100,18 @@ public class RecentsProvider extends ContentProvider {
private static final int VERSION_INIT = 1;
private static final int VERSION_AS_BLOB = 3;
private static final int VERSION_ADD_EXTERNAL = 4;
+ private static final int VERSION_ADD_RECENT_KEY = 5;
public DatabaseHelper(Context context) {
- super(context, DB_NAME, null, VERSION_ADD_EXTERNAL);
+ super(context, DB_NAME, null, VERSION_ADD_RECENT_KEY);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_RECENT + " (" +
- RecentColumns.STACK + " BLOB PRIMARY KEY ON CONFLICT REPLACE," +
+ RecentColumns.KEY + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
+ RecentColumns.STACK + " BLOB DEFAULT NULL," +
RecentColumns.TIMESTAMP + " INTEGER" +
")");
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 15af8aa..e3908e9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -99,7 +99,8 @@ public class RootsCache {
*/
public void updateAsync() {
// Special root for recents
- mRecentsRoot.rootType = Root.ROOT_TYPE_SHORTCUT;
+ mRecentsRoot.authority = null;
+ mRecentsRoot.rootId = null;
mRecentsRoot.icon = R.drawable.ic_root_recent;
mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE;
mRecentsRoot.title = mContext.getString(R.string.root_recent);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index d602622..2fb12bb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -26,7 +26,6 @@ import android.content.Loader;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
-import android.provider.DocumentsContract.Root;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.view.LayoutInflater;
@@ -37,16 +36,16 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
-import android.widget.Space;
import android.widget.TextView;
import com.android.documentsui.DocumentsActivity.State;
-import com.android.documentsui.SectionedListAdapter.SectionAdapter;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
import com.android.internal.util.Objects;
+import com.google.common.collect.Lists;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -56,7 +55,7 @@ import java.util.List;
public class RootsFragment extends Fragment {
private ListView mList;
- private SectionedRootsAdapter mAdapter;
+ private RootsAdapter mAdapter;
private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
@@ -112,7 +111,7 @@ public class RootsFragment extends Fragment {
final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
- mAdapter = new SectionedRootsAdapter(context, result, includeApps);
+ mAdapter = new RootsAdapter(context, result, includeApps);
mList.setAdapter(mAdapter);
onCurrentRootChanged();
@@ -154,136 +153,148 @@ public class RootsFragment extends Fragment {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
- final Object item = mAdapter.getItem(position);
- if (item instanceof RootInfo) {
- activity.onRootPicked((RootInfo) item, true);
- } else if (item instanceof ResolveInfo) {
- activity.onAppPicked((ResolveInfo) item);
+ final Item item = mAdapter.getItem(position);
+ if (item instanceof RootItem) {
+ activity.onRootPicked(((RootItem) item).root, true);
+ } else if (item instanceof AppItem) {
+ activity.onAppPicked(((AppItem) item).info);
} else {
throw new IllegalStateException("Unknown root: " + item);
}
}
};
- private static class RootsAdapter extends ArrayAdapter<RootInfo> implements SectionAdapter {
- public RootsAdapter(Context context) {
- super(context, 0);
+ private static abstract class Item {
+ private final int mLayoutId;
+
+ public Item(int layoutId) {
+ mLayoutId = layoutId;
}
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final Context context = parent.getContext();
+ public View getView(View convertView, ViewGroup parent) {
if (convertView == null) {
- convertView = LayoutInflater.from(context)
- .inflate(R.layout.item_root, parent, false);
+ convertView = LayoutInflater.from(parent.getContext())
+ .inflate(mLayoutId, parent, false);
}
+ bindView(convertView);
+ return convertView;
+ }
+
+ public abstract void bindView(View convertView);
+ }
+ private static class RootItem extends Item {
+ public final RootInfo root;
+
+ public RootItem(RootInfo root) {
+ super(R.layout.item_root);
+ this.root = root;
+ }
+
+ @Override
+ public void bindView(View convertView) {
final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
- final RootInfo root = getItem(position);
+ final Context context = convertView.getContext();
icon.setImageDrawable(root.loadIcon(context));
title.setText(root.title);
- // Device summary is always available space
- final String summaryText;
- if (root.rootType == Root.ROOT_TYPE_DEVICE && root.availableBytes >= 0) {
+ // Show available space if no summary
+ String summaryText = root.summary;
+ if (TextUtils.isEmpty(summaryText) && root.availableBytes >= 0) {
summaryText = context.getString(R.string.root_available_bytes,
Formatter.formatFileSize(context, root.availableBytes));
- } else {
- summaryText = root.summary;
}
summary.setText(summaryText);
summary.setVisibility(TextUtils.isEmpty(summaryText) ? View.GONE : View.VISIBLE);
+ }
+ }
- return convertView;
+ private static class SpacerItem extends Item {
+ public SpacerItem() {
+ super(R.layout.item_root_spacer);
}
@Override
- public View getHeaderView(View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = new Space(parent.getContext());
- }
- return convertView;
+ public void bindView(View convertView) {
+ // Nothing to bind
}
}
- private static class AppsAdapter extends ArrayAdapter<ResolveInfo> implements SectionAdapter {
- public AppsAdapter(Context context) {
- super(context, 0);
+ private static class AppItem extends Item {
+ public final ResolveInfo info;
+
+ public AppItem(ResolveInfo info) {
+ super(R.layout.item_root);
+ this.info = info;
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final Context context = parent.getContext();
- final PackageManager pm = context.getPackageManager();
- if (convertView == null) {
- convertView = LayoutInflater.from(context)
- .inflate(R.layout.item_root, parent, false);
- }
-
+ public void bindView(View convertView) {
final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
- final ResolveInfo info = getItem(position);
+ final PackageManager pm = convertView.getContext().getPackageManager();
icon.setImageDrawable(info.loadIcon(pm));
title.setText(info.loadLabel(pm));
// TODO: match existing summary behavior from disambig dialog
summary.setVisibility(View.GONE);
-
- return convertView;
}
+ }
- @Override
- public View getHeaderView(View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.item_root_header, parent, false);
- }
+ private static class RootsAdapter extends ArrayAdapter<Item> {
+ public RootsAdapter(Context context, Collection<RootInfo> roots, Intent includeApps) {
+ super(context, 0);
- final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- title.setText(R.string.root_type_apps);
+ RootItem recents = null;
+ RootItem images = null;
+ RootItem videos = null;
+ RootItem audio = null;
+ RootItem downloads = null;
- return convertView;
- }
- }
-
- private static class SectionedRootsAdapter extends SectionedListAdapter {
- private final RootsAdapter mRecent;
- private final RootsAdapter mServices;
- private final RootsAdapter mShortcuts;
- private final RootsAdapter mDevices;
- private final AppsAdapter mApps;
-
- public SectionedRootsAdapter(
- Context context, Collection<RootInfo> roots, Intent includeApps) {
- mRecent = new RootsAdapter(context);
- mServices = new RootsAdapter(context);
- mShortcuts = new RootsAdapter(context);
- mDevices = new RootsAdapter(context);
- mApps = new AppsAdapter(context);
+ final List<RootInfo> clouds = Lists.newArrayList();
+ final List<RootInfo> locals = Lists.newArrayList();
for (RootInfo root : roots) {
- if (root.authority == null) {
- mRecent.add(root);
- continue;
+ if (root.isRecents()) {
+ recents = new RootItem(root);
+ } else if (root.isExternalStorage()) {
+ locals.add(root);
+ } else if (root.isDownloads()) {
+ downloads = new RootItem(root);
+ } else if (root.isImages()) {
+ images = new RootItem(root);
+ } else if (root.isVideos()) {
+ videos = new RootItem(root);
+ } else if (root.isAudio()) {
+ audio = new RootItem(root);
+ } else {
+ clouds.add(root);
}
+ }
- switch (root.rootType) {
- case Root.ROOT_TYPE_SERVICE:
- mServices.add(root);
- break;
- case Root.ROOT_TYPE_SHORTCUT:
- mShortcuts.add(root);
- break;
- case Root.ROOT_TYPE_DEVICE:
- mDevices.add(root);
- break;
- }
+ final RootComparator comp = new RootComparator();
+ Collections.sort(clouds, comp);
+ Collections.sort(locals, comp);
+
+ if (recents != null) add(recents);
+
+ for (RootInfo cloud : clouds) {
+ add(new RootItem(cloud));
+ }
+
+ if (images != null) add(images);
+ if (videos != null) add(videos);
+ if (audio != null) add(audio);
+ if (downloads != null) add(downloads);
+
+ for (RootInfo local : locals) {
+ add(new RootItem(local));
}
if (includeApps != null) {
@@ -291,35 +302,54 @@ public class RootsFragment extends Fragment {
final List<ResolveInfo> infos = pm.queryIntentActivities(
includeApps, PackageManager.MATCH_DEFAULT_ONLY);
+ final List<AppItem> apps = Lists.newArrayList();
+
// Omit ourselves from the list
for (ResolveInfo info : infos) {
if (!context.getPackageName().equals(info.activityInfo.packageName)) {
- mApps.add(info);
+ apps.add(new AppItem(info));
+ }
+ }
+
+ if (apps.size() > 0) {
+ add(new SpacerItem());
+ for (Item item : apps) {
+ add(item);
}
}
}
+ }
- final RootComparator comp = new RootComparator();
- mServices.sort(comp);
- mShortcuts.sort(comp);
- mDevices.sort(comp);
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final Item item = getItem(position);
+ return item.getView(convertView, parent);
+ }
- if (mRecent.getCount() > 0) {
- addSection(mRecent);
- }
- if (mServices.getCount() > 0) {
- addSection(mServices);
- }
- if (mShortcuts.getCount() > 0) {
- addSection(mShortcuts);
- }
- if (mDevices.getCount() > 0) {
- addSection(mDevices);
- }
- if (mApps.getCount() > 0) {
- addSection(mApps);
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return getItemViewType(position) != 1;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ final Item item = getItem(position);
+ if (item instanceof RootItem || item instanceof AppItem) {
+ return 0;
+ } else {
+ return 1;
}
}
+
+ @Override
+ public int getViewTypeCount() {
+ return 2;
+ }
}
public static class RootComparator implements Comparator<RootInfo> {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
index 57fc7e4..1a47308 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
@@ -213,8 +213,13 @@ public class TestActivity extends Activity {
if (DocumentsContract.isDocumentUri(this, uri)) {
result += "; DOC_ID";
}
- getContentResolver()
- .takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ try {
+ getContentResolver().takePersistableUriPermission(
+ uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } catch (SecurityException e) {
+ result += "; FAILED TO TAKE";
+ Log.e(TAG, "Failed to take", e);
+ }
InputStream is = null;
try {
is = getContentResolver().openInputStream(uri);
@@ -222,7 +227,7 @@ public class TestActivity extends Activity {
result += "; read length=" + length;
} catch (Exception e) {
result += "; ERROR";
- Log.w(TAG, "Failed to read " + uri, e);
+ Log.e(TAG, "Failed to read " + uri, e);
} finally {
IoUtils.closeQuietly(is);
}
@@ -235,15 +240,20 @@ public class TestActivity extends Activity {
if (DocumentsContract.isDocumentUri(this, uri)) {
result += "; DOC_ID";
}
- getContentResolver()
- .takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ try {
+ getContentResolver().takePersistableUriPermission(
+ uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ } catch (SecurityException e) {
+ result += "; FAILED TO TAKE";
+ Log.e(TAG, "Failed to take", e);
+ }
OutputStream os = null;
try {
os = getContentResolver().openOutputStream(uri);
os.write("THE COMPLETE WORKS OF SHAKESPEARE".getBytes());
} catch (Exception e) {
result += "; ERROR";
- Log.w(TAG, "Failed to write " + uri, e);
+ Log.e(TAG, "Failed to write " + uri, e);
} finally {
IoUtils.closeQuietly(os);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
index 0a378c0..28bab6c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
@@ -71,6 +71,25 @@ public class DocumentStack extends LinkedList<DocumentInfo> implements Durable {
}
}
+ /**
+ * Build key that uniquely identifies this stack. It omits most of the raw
+ * details included in {@link #write(DataOutputStream)}, since they change
+ * too regularly to be used as a key.
+ */
+ public String buildKey() {
+ final StringBuilder builder = new StringBuilder();
+ if (root != null) {
+ builder.append(root.authority).append('#');
+ builder.append(root.rootId).append('#');
+ } else {
+ builder.append("[null]").append('#');
+ }
+ for (DocumentInfo doc : this) {
+ builder.append(doc.documentId).append('#');
+ }
+ return builder.toString();
+ }
+
@Override
public void reset() {
clear();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 014901a..e220c9e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -42,10 +42,10 @@ import java.util.Objects;
*/
public class RootInfo implements Durable, Parcelable {
private static final int VERSION_INIT = 1;
+ private static final int VERSION_DROP_TYPE = 2;
public String authority;
public String rootId;
- public int rootType;
public int flags;
public int icon;
public String title;
@@ -67,7 +67,6 @@ public class RootInfo implements Durable, Parcelable {
public void reset() {
authority = null;
rootId = null;
- rootType = 0;
flags = 0;
icon = 0;
title = null;
@@ -85,10 +84,9 @@ public class RootInfo implements Durable, Parcelable {
public void read(DataInputStream in) throws IOException {
final int version = in.readInt();
switch (version) {
- case VERSION_INIT:
+ case VERSION_DROP_TYPE:
authority = DurableUtils.readNullableString(in);
rootId = DurableUtils.readNullableString(in);
- rootType = in.readInt();
flags = in.readInt();
icon = in.readInt();
title = DurableUtils.readNullableString(in);
@@ -105,10 +103,9 @@ public class RootInfo implements Durable, Parcelable {
@Override
public void write(DataOutputStream out) throws IOException {
- out.writeInt(VERSION_INIT);
+ out.writeInt(VERSION_DROP_TYPE);
DurableUtils.writeNullableString(out, authority);
DurableUtils.writeNullableString(out, rootId);
- out.writeInt(rootType);
out.writeInt(flags);
out.writeInt(icon);
DurableUtils.writeNullableString(out, title);
@@ -146,7 +143,6 @@ public class RootInfo implements Durable, Parcelable {
final RootInfo root = new RootInfo();
root.authority = authority;
root.rootId = getCursorString(cursor, Root.COLUMN_ROOT_ID);
- root.rootType = getCursorInt(cursor, Root.COLUMN_ROOT_TYPE);
root.flags = getCursorInt(cursor, Root.COLUMN_FLAGS);
root.icon = getCursorInt(cursor, Root.COLUMN_ICON);
root.title = getCursorString(cursor, Root.COLUMN_TITLE);
@@ -162,25 +158,44 @@ public class RootInfo implements Durable, Parcelable {
derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;
// TODO: remove these special case icons
- if ("com.android.externalstorage.documents".equals(authority)) {
- if ("documents".equals(rootId)) {
- derivedIcon = R.drawable.ic_doc_text;
- } else {
- derivedIcon = R.drawable.ic_root_sdcard;
- }
- }
- if ("com.android.providers.downloads.documents".equals(authority)) {
+ if (isExternalStorage()) {
+ derivedIcon = R.drawable.ic_root_sdcard;
+ } else if (isDownloads()) {
derivedIcon = R.drawable.ic_root_download;
+ } else if (isImages()) {
+ derivedIcon = R.drawable.ic_doc_image;
+ } else if (isVideos()) {
+ derivedIcon = R.drawable.ic_doc_video;
+ } else if (isAudio()) {
+ derivedIcon = R.drawable.ic_doc_audio;
}
- if ("com.android.providers.media.documents".equals(authority)) {
- if ("images_root".equals(rootId)) {
- derivedIcon = R.drawable.ic_doc_image;
- } else if ("videos_root".equals(rootId)) {
- derivedIcon = R.drawable.ic_doc_video;
- } else if ("audio_root".equals(rootId)) {
- derivedIcon = R.drawable.ic_doc_audio;
- }
- }
+ }
+
+ public boolean isRecents() {
+ return authority == null && rootId == null;
+ }
+
+ public boolean isExternalStorage() {
+ return "com.android.externalstorage.documents".equals(authority);
+ }
+
+ public boolean isDownloads() {
+ return "com.android.providers.downloads.documents".equals(authority);
+ }
+
+ public boolean isImages() {
+ return "com.android.providers.media.documents".equals(authority)
+ && "images_root".equals(rootId);
+ }
+
+ public boolean isVideos() {
+ return "com.android.providers.media.documents".equals(authority)
+ && "videos_root".equals(rootId);
+ }
+
+ public boolean isAudio() {
+ return "com.android.providers.media.documents".equals(authority)
+ && "audio_root".equals(rootId);
}
@Override