Add "Home" directory support.

Update FilesActivityUiTests to verify Home is present
    and that clicking a root sets the title accordingly.
Guard addition of WRITABLE flag with a volume test.

Bug: 25147243
Change-Id: Ic20372737cae08a82af0aade0c0dbbd8c22d5f34
This commit is contained in:
Steve McKay
2015-11-18 14:56:50 -08:00
parent c38a5d7da1
commit c6a4cd8c0f
7 changed files with 112 additions and 23 deletions

View File

@@ -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>

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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>

View File

@@ -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;