Network browsing (#25)
Add basic network browsing support. The embedded "master browser" functionality embedded in libsmbclient is the only way to browse as of now, but the code is structured to be extendable for other mechanisms of browsing. This fixes #5.
This commit is contained in:
parent
ae3b679a9c
commit
f5fc69d1c2
13 changed files with 441 additions and 10 deletions
|
@ -27,6 +27,7 @@ import android.net.NetworkRequest;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.google.android.sambadocumentsprovider.SambaConfiguration.OnConfigurationChangedListener;
|
import com.google.android.sambadocumentsprovider.SambaConfiguration.OnConfigurationChangedListener;
|
||||||
|
import com.google.android.sambadocumentsprovider.browsing.NetworkBrowser;
|
||||||
import com.google.android.sambadocumentsprovider.cache.DocumentCache;
|
import com.google.android.sambadocumentsprovider.cache.DocumentCache;
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.CredentialCache;
|
import com.google.android.sambadocumentsprovider.nativefacade.CredentialCache;
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.SambaMessageLooper;
|
import com.google.android.sambadocumentsprovider.nativefacade.SambaMessageLooper;
|
||||||
|
@ -42,6 +43,7 @@ public class SambaProviderApplication extends Application {
|
||||||
|
|
||||||
private SmbFacade mSambaClient;
|
private SmbFacade mSambaClient;
|
||||||
private ShareManager mShareManager;
|
private ShareManager mShareManager;
|
||||||
|
private NetworkBrowser mNetworkBrowser;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
|
@ -64,6 +66,8 @@ public class SambaProviderApplication extends Application {
|
||||||
|
|
||||||
mShareManager = new ShareManager(context, credentialCache);
|
mShareManager = new ShareManager(context, credentialCache);
|
||||||
|
|
||||||
|
mNetworkBrowser = new NetworkBrowser(mSambaClient, mTaskManager);
|
||||||
|
|
||||||
registerNetworkCallback(context);
|
registerNetworkCallback(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +133,10 @@ public class SambaProviderApplication extends Application {
|
||||||
return getApplication(context).mTaskManager;
|
return getApplication(context).mTaskManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static NetworkBrowser getNetworkBrowser(Context context) {
|
||||||
|
return getApplication(context).mNetworkBrowser;
|
||||||
|
}
|
||||||
|
|
||||||
private static SambaProviderApplication getApplication(Context context) {
|
private static SambaProviderApplication getApplication(Context context) {
|
||||||
return ((SambaProviderApplication) context.getApplicationContext());
|
return ((SambaProviderApplication) context.getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,6 @@ import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.AsyncTask.Status;
|
import android.os.AsyncTask.Status;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.google.android.sambadocumentsprovider.provider.ReadFileTask;
|
|
||||||
import com.google.android.sambadocumentsprovider.provider.WriteFileTask;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class DirectoryEntry {
|
||||||
|
|
||||||
@IntDef({WORKGROUP, SERVER, FILE_SHARE, PRINTER_SHARE, COMMS_SHARE, IPC_SHARE, DIR, FILE, LINK})
|
@IntDef({WORKGROUP, SERVER, FILE_SHARE, PRINTER_SHARE, COMMS_SHARE, IPC_SHARE, DIR, FILE, LINK})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@interface Type {}
|
public @interface Type {}
|
||||||
public static final int WORKGROUP = 1;
|
public static final int WORKGROUP = 1;
|
||||||
public static final int SERVER = 2;
|
public static final int SERVER = 2;
|
||||||
public static final int FILE_SHARE = 3;
|
public static final int FILE_SHARE = 3;
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.sambadocumentsprovider.browsing;
|
||||||
|
|
||||||
|
class BrowsingException extends Exception {
|
||||||
|
BrowsingException(String message) {
|
||||||
|
super("Browsing failed: " + message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.sambadocumentsprovider.browsing;
|
||||||
|
|
||||||
|
import com.google.android.sambadocumentsprovider.base.DirectoryEntry;
|
||||||
|
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
||||||
|
import com.google.android.sambadocumentsprovider.nativefacade.SmbDir;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class MasterBrowsingProvider implements NetworkBrowsingProvider {
|
||||||
|
private static final String MASTER_BROWSING_DIR = "smb://";
|
||||||
|
|
||||||
|
private final SmbClient mClient;
|
||||||
|
|
||||||
|
MasterBrowsingProvider(SmbClient client) {
|
||||||
|
mClient = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getServers() throws BrowsingException {
|
||||||
|
List<String> serversList = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
SmbDir rootDir = mClient.openDir(MASTER_BROWSING_DIR);
|
||||||
|
|
||||||
|
List<DirectoryEntry> workgroups = getDirectoryChildren(rootDir);
|
||||||
|
for (DirectoryEntry workgroup : workgroups) {
|
||||||
|
if (workgroup.getType() == DirectoryEntry.WORKGROUP) {
|
||||||
|
List<DirectoryEntry> servers = getDirectoryChildren
|
||||||
|
(mClient.openDir(MASTER_BROWSING_DIR + workgroup.getName()));
|
||||||
|
|
||||||
|
for (DirectoryEntry server : servers) {
|
||||||
|
if (server.getType() == DirectoryEntry.SERVER) {
|
||||||
|
serversList.add(server.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new BrowsingException(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return serversList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<DirectoryEntry> getDirectoryChildren(SmbDir dir) throws IOException {
|
||||||
|
List<DirectoryEntry> children = new ArrayList<>();
|
||||||
|
|
||||||
|
DirectoryEntry currentEntry;
|
||||||
|
while ((currentEntry = dir.readDir()) != null) {
|
||||||
|
children.add(currentEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.sambadocumentsprovider.browsing;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.sambadocumentsprovider.TaskManager;
|
||||||
|
import com.google.android.sambadocumentsprovider.base.DirectoryEntry;
|
||||||
|
import com.google.android.sambadocumentsprovider.base.OnTaskFinishedCallback;
|
||||||
|
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
||||||
|
import com.google.android.sambadocumentsprovider.nativefacade.SmbDir;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
public class NetworkBrowser {
|
||||||
|
public static final Uri SMB_BROWSING_URI = Uri.parse("smb://");
|
||||||
|
|
||||||
|
private static final String TAG = "NetworkBrowser";
|
||||||
|
|
||||||
|
private final NetworkBrowsingProvider mMasterProvider;
|
||||||
|
private final TaskManager mTaskManager;
|
||||||
|
|
||||||
|
private final Map<Uri, Future> mTasks = new HashMap<>();
|
||||||
|
|
||||||
|
public NetworkBrowser(SmbClient client, TaskManager taskManager) {
|
||||||
|
mMasterProvider = new MasterBrowsingProvider(client);
|
||||||
|
mTaskManager = taskManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncTask getServersAsync(OnTaskFinishedCallback<List<String>> callback) {
|
||||||
|
AsyncTask<Void, Void, List<String>> loadServersTask = new LoadServersTask(callback);
|
||||||
|
|
||||||
|
mTaskManager.runTask(SMB_BROWSING_URI, loadServersTask);
|
||||||
|
|
||||||
|
return loadServersTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getServers() throws BrowsingException {
|
||||||
|
return mMasterProvider.getServers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LoadServersTask extends AsyncTask<Void, Void, List<String>> {
|
||||||
|
final OnTaskFinishedCallback<List<String>> mCallback;
|
||||||
|
|
||||||
|
private BrowsingException mException;
|
||||||
|
|
||||||
|
LoadServersTask(OnTaskFinishedCallback<List<String>> callback) {
|
||||||
|
mCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> loadData() throws BrowsingException {
|
||||||
|
return getServers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> doInBackground(Void... voids) {
|
||||||
|
try {
|
||||||
|
return loadData();
|
||||||
|
} catch (BrowsingException e) {
|
||||||
|
Log.e(TAG, "Failed to load data for network browsing: ", e);
|
||||||
|
mException = e;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(List<String> servers) {
|
||||||
|
if (servers != null) {
|
||||||
|
mCallback.onTaskFinished(OnTaskFinishedCallback.SUCCEEDED, servers, null);
|
||||||
|
} else {
|
||||||
|
mCallback.onTaskFinished(OnTaskFinishedCallback.FAILED, null, mException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google Inc.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.sambadocumentsprovider.browsing;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
interface NetworkBrowsingProvider {
|
||||||
|
/**
|
||||||
|
* Returns unresolved host names.
|
||||||
|
*/
|
||||||
|
List<String> getServers() throws BrowsingException;
|
||||||
|
}
|
|
@ -25,6 +25,8 @@ import android.system.StructStat;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import com.google.android.sambadocumentsprovider.R;
|
||||||
import com.google.android.sambadocumentsprovider.base.DirectoryEntry;
|
import com.google.android.sambadocumentsprovider.base.DirectoryEntry;
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbDir;
|
import com.google.android.sambadocumentsprovider.nativefacade.SmbDir;
|
||||||
|
@ -32,7 +34,6 @@ import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
@ -74,6 +75,18 @@ public class DocumentMetadata {
|
||||||
return mEntry.getType() == DirectoryEntry.FILE_SHARE;
|
return mEntry.getType() == DirectoryEntry.FILE_SHARE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getIconResourceId() {
|
||||||
|
switch (mEntry.getType()) {
|
||||||
|
case DirectoryEntry.SERVER:
|
||||||
|
return R.drawable.ic_server;
|
||||||
|
case DirectoryEntry.FILE_SHARE:
|
||||||
|
return R.drawable.ic_folder_shared;
|
||||||
|
default:
|
||||||
|
// Tells SAF to use the default icon.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Long getLastModified() {
|
public Long getLastModified() {
|
||||||
final StructStat stat = mStat.get();
|
final StructStat stat = mStat.get();
|
||||||
return (stat == null) ? null : TimeUnit.MILLISECONDS.convert(stat.st_mtime, TimeUnit.SECONDS);
|
return (stat == null) ? null : TimeUnit.MILLISECONDS.convert(stat.st_mtime, TimeUnit.SECONDS);
|
||||||
|
@ -293,6 +306,10 @@ public class DocumentMetadata {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isServerUri(Uri uri) {
|
||||||
|
return uri.getPathSegments().isEmpty() && !uri.getAuthority().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public static DocumentMetadata fromUri(Uri uri, SmbClient client) throws IOException {
|
public static DocumentMetadata fromUri(Uri uri, SmbClient client) throws IOException {
|
||||||
final List<String> pathSegments = uri.getPathSegments();
|
final List<String> pathSegments = uri.getPathSegments();
|
||||||
if (pathSegments.isEmpty()) {
|
if (pathSegments.isEmpty()) {
|
||||||
|
@ -315,9 +332,17 @@ public class DocumentMetadata {
|
||||||
return createShare(uri);
|
return createShare(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DocumentMetadata createServer(Uri uri) {
|
||||||
|
return create(uri, DirectoryEntry.SERVER);
|
||||||
|
}
|
||||||
|
|
||||||
public static DocumentMetadata createShare(Uri uri) {
|
public static DocumentMetadata createShare(Uri uri) {
|
||||||
|
return create(uri, DirectoryEntry.FILE_SHARE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DocumentMetadata create(Uri uri, @DirectoryEntry.Type int type) {
|
||||||
final DirectoryEntry entry =
|
final DirectoryEntry entry =
|
||||||
new DirectoryEntry(DirectoryEntry.FILE_SHARE, "", uri.getLastPathSegment());
|
new DirectoryEntry(type, "", uri.getLastPathSegment());
|
||||||
return new DocumentMetadata(uri, entry);
|
return new DocumentMetadata(uri, entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ import com.google.android.sambadocumentsprovider.cache.DocumentCache;
|
||||||
import com.google.android.sambadocumentsprovider.document.DocumentMetadata;
|
import com.google.android.sambadocumentsprovider.document.DocumentMetadata;
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
||||||
import com.google.android.sambadocumentsprovider.provider.SambaDocumentsProvider;
|
import com.google.android.sambadocumentsprovider.provider.SambaDocumentsProvider;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class MountServerActivity extends AppCompatActivity {
|
public class MountServerActivity extends AppCompatActivity {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.MatrixCursor;
|
import android.database.MatrixCursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
|
@ -48,6 +49,7 @@ import com.google.android.sambadocumentsprovider.TaskManager;
|
||||||
import com.google.android.sambadocumentsprovider.base.AuthFailedException;
|
import com.google.android.sambadocumentsprovider.base.AuthFailedException;
|
||||||
import com.google.android.sambadocumentsprovider.base.DirectoryEntry;
|
import com.google.android.sambadocumentsprovider.base.DirectoryEntry;
|
||||||
import com.google.android.sambadocumentsprovider.base.DocumentCursor;
|
import com.google.android.sambadocumentsprovider.base.DocumentCursor;
|
||||||
|
import com.google.android.sambadocumentsprovider.browsing.NetworkBrowser;
|
||||||
import com.google.android.sambadocumentsprovider.cache.CacheResult;
|
import com.google.android.sambadocumentsprovider.cache.CacheResult;
|
||||||
import com.google.android.sambadocumentsprovider.cache.DocumentCache;
|
import com.google.android.sambadocumentsprovider.cache.DocumentCache;
|
||||||
import com.google.android.sambadocumentsprovider.document.DocumentMetadata;
|
import com.google.android.sambadocumentsprovider.document.DocumentMetadata;
|
||||||
|
@ -55,12 +57,11 @@ import com.google.android.sambadocumentsprovider.document.LoadChildrenTask;
|
||||||
import com.google.android.sambadocumentsprovider.base.OnTaskFinishedCallback;
|
import com.google.android.sambadocumentsprovider.base.OnTaskFinishedCallback;
|
||||||
import com.google.android.sambadocumentsprovider.document.LoadDocumentTask;
|
import com.google.android.sambadocumentsprovider.document.LoadDocumentTask;
|
||||||
import com.google.android.sambadocumentsprovider.document.LoadStatTask;
|
import com.google.android.sambadocumentsprovider.document.LoadStatTask;
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbFacade;
|
import com.google.android.sambadocumentsprovider.nativefacade.SmbFacade;
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbFile;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -87,7 +88,8 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
Document.COLUMN_FLAGS,
|
Document.COLUMN_FLAGS,
|
||||||
Document.COLUMN_MIME_TYPE,
|
Document.COLUMN_MIME_TYPE,
|
||||||
Document.COLUMN_SIZE,
|
Document.COLUMN_SIZE,
|
||||||
Document.COLUMN_LAST_MODIFIED
|
Document.COLUMN_LAST_MODIFIED,
|
||||||
|
Document.COLUMN_ICON
|
||||||
};
|
};
|
||||||
|
|
||||||
private final OnTaskFinishedCallback<Uri> mLoadDocumentCallback =
|
private final OnTaskFinishedCallback<Uri> mLoadDocumentCallback =
|
||||||
|
@ -127,6 +129,22 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private final OnTaskFinishedCallback<List<String>> mLoadSharesFinishedCallback =
|
||||||
|
new OnTaskFinishedCallback<List<String>>() {
|
||||||
|
@Override
|
||||||
|
public void onTaskFinished(
|
||||||
|
@OnTaskFinishedCallback.Status int status,
|
||||||
|
@Nullable List<String> item,
|
||||||
|
@Nullable Exception exception) {
|
||||||
|
if (BuildConfig.DEBUG) Log.d(TAG, "Browsing callback");
|
||||||
|
|
||||||
|
mBrowsingStorage = item;
|
||||||
|
|
||||||
|
getContext().getContentResolver().notifyChange(
|
||||||
|
toNotifyUri(toUri(NetworkBrowser.SMB_BROWSING_URI.toString())), null, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final MountedShareChangeListener mShareChangeListener = new MountedShareChangeListener() {
|
private final MountedShareChangeListener mShareChangeListener = new MountedShareChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onMountedServerChange() {
|
public void onMountedServerChange() {
|
||||||
|
@ -142,6 +160,9 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
private DocumentCache mCache;
|
private DocumentCache mCache;
|
||||||
private TaskManager mTaskManager;
|
private TaskManager mTaskManager;
|
||||||
private StorageManager mStorageManager;
|
private StorageManager mStorageManager;
|
||||||
|
private NetworkBrowser mNetworkBrowser;
|
||||||
|
|
||||||
|
private List<String> mBrowsingStorage = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
|
@ -155,6 +176,7 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
mShareManager = SambaProviderApplication.getServerManager(context);
|
mShareManager = SambaProviderApplication.getServerManager(context);
|
||||||
mShareManager.addListener(mShareChangeListener);
|
mShareManager.addListener(mShareChangeListener);
|
||||||
mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
|
mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
|
||||||
|
mNetworkBrowser = SambaProviderApplication.getNetworkBrowser(context);
|
||||||
|
|
||||||
return mClient != null;
|
return mClient != null;
|
||||||
}
|
}
|
||||||
|
@ -165,6 +187,15 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
projection = (projection == null) ? DEFAULT_ROOT_PROJECTION : projection;
|
projection = (projection == null) ? DEFAULT_ROOT_PROJECTION : projection;
|
||||||
|
|
||||||
MatrixCursor cursor = new MatrixCursor(projection, mShareManager.size());
|
MatrixCursor cursor = new MatrixCursor(projection, mShareManager.size());
|
||||||
|
|
||||||
|
cursor.addRow(new Object[] {
|
||||||
|
NetworkBrowser.SMB_BROWSING_URI.toString(),
|
||||||
|
NetworkBrowser.SMB_BROWSING_URI.toString(),
|
||||||
|
getContext().getResources().getString(R.string.browsing_root_name),
|
||||||
|
0,
|
||||||
|
R.drawable.ic_cloud,
|
||||||
|
});
|
||||||
|
|
||||||
for (String uri : mShareManager) {
|
for (String uri : mShareManager) {
|
||||||
final String name;
|
final String name;
|
||||||
final Uri parsedUri = Uri.parse(uri);
|
final Uri parsedUri = Uri.parse(uri);
|
||||||
|
@ -204,7 +235,15 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, "Querying document: " + documentId);
|
if (BuildConfig.DEBUG) Log.d(TAG, "Querying document: " + documentId);
|
||||||
projection = (projection == null) ? DEFAULT_DOCUMENT_PROJECTION : projection;
|
projection = (projection == null) ? DEFAULT_DOCUMENT_PROJECTION : projection;
|
||||||
|
|
||||||
|
final MatrixCursor cursor = new MatrixCursor(projection);
|
||||||
final Uri uri = toUri(documentId);
|
final Uri uri = toUri(documentId);
|
||||||
|
|
||||||
|
if (documentId.equals(NetworkBrowser.SMB_BROWSING_URI.toString())) {
|
||||||
|
cursor.addRow(getCursorRowForBrowsingRoot(projection));
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
try (CacheResult result = mCache.get(uri)) {
|
try (CacheResult result = mCache.get(uri)) {
|
||||||
|
|
||||||
|
@ -221,7 +260,6 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
metadata = result.getItem();
|
metadata = result.getItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
final MatrixCursor cursor = new MatrixCursor(projection);
|
|
||||||
cursor.addRow(getDocumentValues(projection, metadata));
|
cursor.addRow(getDocumentValues(projection, metadata));
|
||||||
|
|
||||||
return cursor;
|
return cursor;
|
||||||
|
@ -239,13 +277,27 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, "Querying children documents under " + documentId);
|
if (BuildConfig.DEBUG) Log.d(TAG, "Querying children documents under " + documentId);
|
||||||
projection = (projection == null) ? DEFAULT_DOCUMENT_PROJECTION : projection;
|
projection = (projection == null) ? DEFAULT_DOCUMENT_PROJECTION : projection;
|
||||||
|
|
||||||
|
if (documentId.equals(NetworkBrowser.SMB_BROWSING_URI.toString())) {
|
||||||
|
return getFilesSharesCursor(projection);
|
||||||
|
}
|
||||||
|
|
||||||
final Uri uri = toUri(documentId);
|
final Uri uri = toUri(documentId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (DocumentMetadata.isServerUri(uri)) {
|
||||||
|
try (final CacheResult result = mCache.get(uri)) {
|
||||||
|
if (result.getState() == CacheResult.CACHE_MISS) {
|
||||||
|
DocumentMetadata metadata = DocumentMetadata.createServer(uri);
|
||||||
|
mCache.put(metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try (final CacheResult result = mCache.get(uri)) {
|
try (final CacheResult result = mCache.get(uri)) {
|
||||||
boolean isLoading = false;
|
boolean isLoading = false;
|
||||||
final DocumentCursor cursor = new DocumentCursor(projection);
|
|
||||||
final Bundle extra = new Bundle();
|
final Bundle extra = new Bundle();
|
||||||
final Uri notifyUri = toNotifyUri(uri);
|
final Uri notifyUri = toNotifyUri(uri);
|
||||||
|
final DocumentCursor cursor = new DocumentCursor(projection);
|
||||||
|
|
||||||
if (result.getState() == CacheResult.CACHE_MISS) {
|
if (result.getState() == CacheResult.CACHE_MISS) {
|
||||||
// Last loading failed... Just feed the bitter fruit.
|
// Last loading failed... Just feed the bitter fruit.
|
||||||
|
@ -359,11 +411,76 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
case Document.COLUMN_LAST_MODIFIED:
|
case Document.COLUMN_LAST_MODIFIED:
|
||||||
row[i] = metadata.getLastModified();
|
row[i] = metadata.getLastModified();
|
||||||
break;
|
break;
|
||||||
|
case Document.COLUMN_ICON:
|
||||||
|
row[i] = metadata.getIconResourceId();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Object[] getCursorRowForServer(
|
||||||
|
String[] projection,
|
||||||
|
String server) {
|
||||||
|
Object[] row = new Object[projection.length];
|
||||||
|
|
||||||
|
for (int i = 0; i < projection.length; ++i) {
|
||||||
|
switch (projection[i]) {
|
||||||
|
case Document.COLUMN_DOCUMENT_ID:
|
||||||
|
row[i] = NetworkBrowser.SMB_BROWSING_URI.toString() + server;
|
||||||
|
break;
|
||||||
|
case Document.COLUMN_DISPLAY_NAME:
|
||||||
|
row[i] = server.isEmpty()
|
||||||
|
? getContext().getResources().getString(R.string.browsing_root_name) : server;
|
||||||
|
break;
|
||||||
|
case Document.COLUMN_FLAGS:
|
||||||
|
row[i] = 0;
|
||||||
|
break;
|
||||||
|
case Document.COLUMN_MIME_TYPE:
|
||||||
|
row[i] = Document.MIME_TYPE_DIR;
|
||||||
|
break;
|
||||||
|
case Document.COLUMN_SIZE:
|
||||||
|
case Document.COLUMN_LAST_MODIFIED:
|
||||||
|
row[i] = null;
|
||||||
|
break;
|
||||||
|
case Document.COLUMN_ICON:
|
||||||
|
row[i] = R.drawable.ic_server;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object[] getCursorRowForBrowsingRoot(String[] projection) {
|
||||||
|
return getCursorRowForServer(projection, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Cursor getFilesSharesCursor(String[] projection) {
|
||||||
|
final DocumentCursor cursor = new DocumentCursor(projection);
|
||||||
|
|
||||||
|
final Uri uri = toUri(NetworkBrowser.SMB_BROWSING_URI.toString());
|
||||||
|
|
||||||
|
if (mBrowsingStorage.isEmpty()) {
|
||||||
|
AsyncTask serversTask = mNetworkBrowser.getServersAsync(mLoadSharesFinishedCallback);
|
||||||
|
|
||||||
|
Bundle extra = new Bundle();
|
||||||
|
extra.putBoolean(DocumentsContract.EXTRA_LOADING, true);
|
||||||
|
|
||||||
|
cursor.setNotificationUri(getContext().getContentResolver(), toNotifyUri(uri));
|
||||||
|
cursor.setExtras(extra);
|
||||||
|
cursor.setLoadingTask(serversTask);
|
||||||
|
} else {
|
||||||
|
for (String server : mBrowsingStorage) {
|
||||||
|
cursor.addRow(getCursorRowForServer(projection, server));
|
||||||
|
}
|
||||||
|
|
||||||
|
mBrowsingStorage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String createDocument(String parentDocumentId, String mimeType, String displayName)
|
public String createDocument(String parentDocumentId, String mimeType, String displayName)
|
||||||
throws FileNotFoundException {
|
throws FileNotFoundException {
|
||||||
|
|
28
app/src/main/res/drawable/ic_cloud.xml
Normal file
28
app/src/main/res/drawable/ic_cloud.xml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2017 Google Inc.
|
||||||
|
~
|
||||||
|
~ This program is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by
|
||||||
|
~ the Free Software Foundation, either version 3 of the License, or
|
||||||
|
~ (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful,
|
||||||
|
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
~ GNU General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License
|
||||||
|
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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="#646464"
|
||||||
|
android:pathData="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36
|
||||||
|
0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"/>
|
||||||
|
</vector>
|
31
app/src/main/res/drawable/ic_server.xml
Normal file
31
app/src/main/res/drawable/ic_server.xml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2017 Google Inc.
|
||||||
|
~
|
||||||
|
~ This program is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by
|
||||||
|
~ the Free Software Foundation, either version 3 of the License, or
|
||||||
|
~ (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful,
|
||||||
|
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
~ GNU General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License
|
||||||
|
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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="#FFFFFF"
|
||||||
|
android:pathData="M0 0h24v24H0z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#646464"
|
||||||
|
android:pathData="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0
|
||||||
|
1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"/>
|
||||||
|
</vector>
|
|
@ -47,4 +47,6 @@
|
||||||
<string name="send_feedback">Send feedback</string>
|
<string name="send_feedback">Send feedback</string>
|
||||||
|
|
||||||
<string name="no_web_browser">It needs a web browser to send feedback.</string>
|
<string name="no_web_browser">It needs a web browser to send feedback.</string>
|
||||||
|
|
||||||
|
<string name="browsing_root_name">Samba Shares</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue