diff options
7 files changed, 112 insertions, 23 deletions
| diff --git a/packages/DocumentsUI/res/drawable/ic_root_home.xml b/packages/DocumentsUI/res/drawable/ic_root_home.xml new file mode 100644 index 000000000000..0a258ac65019 --- /dev/null +++ b/packages/DocumentsUI/res/drawable/ic_root_home.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" +    android:width="24dp" +    android:height="24dp" +    android:viewportWidth="24" +    android:viewportHeight="24"> + +    <path +        android:fillColor="#000000" +        android:pathData="M20 6h-8l-2-2H4c-1.1 0-1.99 .9 -1.99 2L2 18c0 1.1 .9 2 2 2h16c1.1 0 2-.9 +2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 +8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z" /> +    <path +        android:pathData="M0 0h24v24H0z" /> +</vector> diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java index beff196509b2..4c844c422ff5 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java @@ -297,7 +297,7 @@ public class RootsFragment extends Fragment {              for (final RootInfo root : roots) {                  final RootItem item = new RootItem(root); -                if (root.isLibrary()) { +                if (root.isLibrary() || root.isHome()) {                      libraries.add(item);                  } else {                      others.add(item); diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java index 723700ded6c6..ae5644d7fc3b 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java @@ -52,7 +52,7 @@ public class RootInfo implements Durable, Parcelable {      public static final int TYPE_DOWNLOADS = 5;      public static final int TYPE_LOCAL = 6;      public static final int TYPE_MTP = 7; -    public static final int TYPE_CLOUD = 8; +    public static final int TYPE_OTHER = 8;      public String authority;      public String rootId; @@ -168,7 +168,10 @@ public class RootInfo implements Durable, Parcelable {          derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;          // TODO: remove these special case icons -        if (isExternalStorage()) { +        if (isHome()) { +            derivedIcon = R.drawable.ic_root_home; +            derivedType = TYPE_LOCAL; +        } else if (isExternalStorage()) {              derivedIcon = R.drawable.ic_root_sdcard;              derivedType = TYPE_LOCAL;          } else if (isDownloads()) { @@ -188,7 +191,7 @@ public class RootInfo implements Durable, Parcelable {          } else if (isMtp()) {              derivedType = TYPE_MTP;          } else { -            derivedType = TYPE_CLOUD; +            derivedType = TYPE_OTHER;          }      } @@ -196,6 +199,13 @@ public class RootInfo implements Durable, Parcelable {          return authority == null && rootId == null;      } +    public boolean isHome() { +        // Note that "home" is the expected root id for the auto-created +        // user home directory on external storage. The "home" value should +        // match ExternalStorageProvider.ROOT_ID_HOME. +        return isExternalStorage() && "home".equals(rootId); +    } +      public boolean isExternalStorage() {          return "com.android.externalstorage.documents".equals(authority);      } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java index ba91c83b95e8..71d8b34f6773 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java @@ -125,6 +125,7 @@ public class FilesActivityUiTest extends InstrumentationTestCase {                  "Videos",                  "Audio",                  "Downloads", +                "Home",                  ROOT_0_ID,                  ROOT_1_ID);      } @@ -136,6 +137,13 @@ public class FilesActivityUiTest extends InstrumentationTestCase {          mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv");      } +    public void testRootClickSetsWindowTitle() throws Exception { +        initTestFiles(); + +        mBot.openRoot("Home"); +        mBot.assertWindowTitle("Home"); +    } +      public void testFilesList_LiveUpdate() throws Exception {          initTestFiles(); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java index 5c09794a3e28..ecad0617c179 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java @@ -16,6 +16,8 @@  package com.android.documentsui; +import static junit.framework.Assert.assertEquals; +  import android.support.test.uiautomator.By;  import android.support.test.uiautomator.BySelector;  import android.support.test.uiautomator.UiDevice; @@ -80,6 +82,20 @@ class UiBot {          mDevice.waitForIdle();      } +    void assertWindowTitle(String expected) { +        // Turns out the title field on a window does not have +        // an id associated with it at runtime (which confuses the hell out of me) +        // In code we address this via "android.R.id.title". +        UiObject2 o = find(By.text(expected)); +        // It's a bit of a conceit that we then *assert* that the title +        // is the value that we used to identify the UiObject2. +        // If the preceeding lookup fails, this'll choke with an NPE. +        // But given the issue described in the comment above, we're +        // going to do it anyway. Because we shouldn't be looking up +        // the uiobject by it's expected content :| +        assertEquals(expected, o.getText()); +    } +      void assertHasRoots(String... labels) throws UiObjectNotFoundException {          List<String> missing = new ArrayList<>();          for (String label : labels) { diff --git a/packages/ExternalStorageProvider/res/values/strings.xml b/packages/ExternalStorageProvider/res/values/strings.xml index f1c1adefc291..e48436ecb8ba 100644 --- a/packages/ExternalStorageProvider/res/values/strings.xml +++ b/packages/ExternalStorageProvider/res/values/strings.xml @@ -20,6 +20,6 @@      <!-- Title for documents backend that offers internal storage. [CHAR LIMIT=24] -->      <string name="root_internal_storage">Internal storage</string> -    <!-- Title for documents backend that offers documents. [CHAR LIMIT=24] --> -    <string name="root_documents">Documents</string> +    <!-- Title for user home dir. [CHAR LIMIT=24] --> +    <string name="root_home">Home</string>  </resources> diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index fcd45f223825..2cedc7238434 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -85,9 +85,11 @@ public class ExternalStorageProvider extends DocumentsProvider {          public String docId;          public File visiblePath;          public File path; +        public boolean reportAvailableBytes = true;      }      private static final String ROOT_ID_PRIMARY_EMULATED = "primary"; +    private static final String ROOT_ID_HOME = "home";      private StorageManager mStorageManager;      private Handler mHandler; @@ -118,6 +120,7 @@ public class ExternalStorageProvider extends DocumentsProvider {      private void updateVolumesLocked() {          mRoots.clear(); +        VolumeInfo primaryVolume = null;          final int userId = UserHandle.myUserId();          final List<VolumeInfo> volumes = mStorageManager.getVolumes();          for (VolumeInfo volume : volumes) { @@ -126,6 +129,9 @@ public class ExternalStorageProvider extends DocumentsProvider {              final String rootId;              final String title;              if (volume.getType() == VolumeInfo.TYPE_EMULATED) { +                // save off the primary volume for subsequent "Home" dir initialization. +                primaryVolume = volume; +                  // We currently only support a single emulated volume mounted at                  // a time, and it's always considered the primary                  rootId = ROOT_ID_PRIMARY_EMULATED; @@ -152,25 +158,58 @@ public class ExternalStorageProvider extends DocumentsProvider {                  continue;              } +            final RootInfo root = new RootInfo(); +            mRoots.put(rootId, root); + +            root.rootId = rootId; +            root.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED +                    | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD; + +            // Dunno when this would NOT be the case, but never hurts to be correct. +            if (volume.isMountedWritable()) { +                root.flags |= Root.FLAG_SUPPORTS_CREATE; +            } +            root.title = title; +            if (volume.getType() == VolumeInfo.TYPE_PUBLIC) { +                root.flags |= Root.FLAG_HAS_SETTINGS; +            } +            if (volume.isVisibleForRead(userId)) { +                root.visiblePath = volume.getPathForUser(userId); +            } else { +                root.visiblePath = null; +            } +            root.path = volume.getInternalPathForUser(userId);              try { -                final RootInfo root = new RootInfo(); -                mRoots.put(rootId, root); - -                root.rootId = rootId; -                root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED -                        | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD; -                root.title = title; -                if (volume.getType() == VolumeInfo.TYPE_PUBLIC) { -                    root.flags |= Root.FLAG_HAS_SETTINGS; -                } -                if (volume.isVisibleForRead(userId)) { -                    root.visiblePath = volume.getPathForUser(userId); -                } else { -                    root.visiblePath = null; -                } -                root.path = volume.getInternalPathForUser(userId);                  root.docId = getDocIdForFile(root.path); +            } catch (FileNotFoundException e) { +                throw new IllegalStateException(e); +            } +        } + +        // Finally, if primary storage is available we add the "Home" directory, +        // creating it as needed. +        if (primaryVolume != null && primaryVolume.isVisible()) { +            final RootInfo root = new RootInfo(); +            root.rootId = ROOT_ID_HOME; +            mRoots.put(root.rootId, root); +            root.title = getContext().getString(R.string.root_home); +            // Only report bytes on *volumes*...as a matter of policy. +            root.reportAvailableBytes = false; +            root.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_SEARCH +                    | Root.FLAG_SUPPORTS_IS_CHILD; + +            // Dunno when this would NOT be the case, but never hurts to be correct. +            if (primaryVolume.isMountedWritable()) { +                root.flags |= Root.FLAG_SUPPORTS_CREATE; +            } + +            root.visiblePath = new File( +                    primaryVolume.getPathForUser(userId), root.rootId); +            root.path = new File( +                    primaryVolume.getInternalPathForUser(userId), root.rootId); +            try { +                root.docId = getDocIdForFile(root.path);              } catch (FileNotFoundException e) {                  throw new IllegalStateException(e);              } @@ -312,7 +351,8 @@ public class ExternalStorageProvider extends DocumentsProvider {                  row.add(Root.COLUMN_FLAGS, root.flags);                  row.add(Root.COLUMN_TITLE, root.title);                  row.add(Root.COLUMN_DOCUMENT_ID, root.docId); -                row.add(Root.COLUMN_AVAILABLE_BYTES, root.path.getFreeSpace()); +                row.add(Root.COLUMN_AVAILABLE_BYTES, +                        root.reportAvailableBytes ? root.path.getFreeSpace() : -1);              }          }          return result; |