diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/browsing/NetworkBrowser.java b/app/src/main/java/com/google/android/sambadocumentsprovider/browsing/NetworkBrowser.java index 15497ce..a3fe864 100644 --- a/app/src/main/java/com/google/android/sambadocumentsprovider/browsing/NetworkBrowser.java +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/browsing/NetworkBrowser.java @@ -22,35 +22,85 @@ 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.browsing.broadcast.BroadcastBrowsingProvider; 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; + +/** + * This class discovers Samba servers and shares under them available on the local network. + */ public class NetworkBrowser { - public static final Uri SMB_BROWSING_URI = Uri.parse("smb://"); + private static final Uri SMB_BROWSING_URI = Uri.parse("smb://"); private static final String TAG = "NetworkBrowser"; private final NetworkBrowsingProvider mMasterProvider; private final NetworkBrowsingProvider mBroadcastProvider; private final TaskManager mTaskManager; + private final SmbClient mClient; public NetworkBrowser(SmbClient client, TaskManager taskManager) { mMasterProvider = new MasterBrowsingProvider(client); mBroadcastProvider = new BroadcastBrowsingProvider(); mTaskManager = taskManager; + mClient = client; } - public AsyncTask getServersAsync(OnTaskFinishedCallback> callback) { - AsyncTask> loadServersTask = new LoadServersTask(callback); + /** + * Asynchronously get available servers and shares under them. + * A server name is mapped to the list of its children. + */ + public AsyncTask getSharesAsync(OnTaskFinishedCallback>> callback) { + AsyncTask>> loadServersTask = new LoadServersTask(callback); mTaskManager.runTask(SMB_BROWSING_URI, loadServersTask); return loadServersTask; } + private Map> getShares() throws BrowsingException { + List servers = getServers(); + + Map> shares = new HashMap<>(); + + for (String server : servers) { + try { + shares.put(server, getSharesForServer(server)); + } catch (IOException e) { + Log.e(TAG, "Failed to load shares for server", e); + } + } + + return shares; + } + + private List getSharesForServer(String server) throws IOException { + List shares = new ArrayList<>(); + + String serverUri = SMB_BROWSING_URI + server; + SmbDir serverDir = mClient.openDir(serverUri); + + DirectoryEntry shareEntry; + while ((shareEntry = serverDir.readDir()) != null) { + if (shareEntry.getType() == DirectoryEntry.FILE_SHARE) { + shares.add(serverUri + "/" + shareEntry.getName().trim()); + } else { + Log.i(TAG, "Unsupported entry type: " + shareEntry.getType()); + } + } + + return shares; + } + private List getServers() throws BrowsingException { List servers = null; @@ -67,23 +117,19 @@ public class NetworkBrowser { return servers; } - private class LoadServersTask extends AsyncTask> { - final OnTaskFinishedCallback> mCallback; + private class LoadServersTask extends AsyncTask>> { + final OnTaskFinishedCallback>> mCallback; private BrowsingException mException; - LoadServersTask(OnTaskFinishedCallback> callback) { + LoadServersTask(OnTaskFinishedCallback>> callback) { mCallback = callback; } - List loadData() throws BrowsingException { - return getServers(); - } - @Override - protected List doInBackground(Void... voids) { + protected Map> doInBackground(Void... voids) { try { - return loadData(); + return getShares(); } catch (BrowsingException e) { Log.e(TAG, "Failed to load data for network browsing: ", e); mException = e; @@ -91,7 +137,7 @@ public class NetworkBrowser { } } - protected void onPostExecute(List servers) { + protected void onPostExecute(Map> servers) { if (servers != null) { mCallback.onTaskFinished(OnTaskFinishedCallback.SUCCEEDED, servers, null); } else { diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/browsing/broadcast/BroadcastBrowsingProvider.java b/app/src/main/java/com/google/android/sambadocumentsprovider/browsing/broadcast/BroadcastBrowsingProvider.java index 96b22d6..d4b8fce 100644 --- a/app/src/main/java/com/google/android/sambadocumentsprovider/browsing/broadcast/BroadcastBrowsingProvider.java +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/browsing/broadcast/BroadcastBrowsingProvider.java @@ -30,7 +30,9 @@ import java.net.InetAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -57,12 +59,12 @@ public class BroadcastBrowsingProvider implements NetworkBrowsingProvider { serverFutures.add(mExecutor.submit(new GetServersForInterfaceTask(address, mTransId))); } - List servers = new ArrayList<>(); + Set servers = new HashSet<>(); for (Future> future : serverFutures) { servers.addAll(future.get()); } - return servers; + return new ArrayList<>(servers); } catch (IOException | ExecutionException | InterruptedException e) { Log.e(TAG, "Failed to get servers via broadcast", e); throw new BrowsingException("Failed to get servers via broadcast", e); diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/mount/BrowsingAutocompleteAdapter.java b/app/src/main/java/com/google/android/sambadocumentsprovider/mount/BrowsingAutocompleteAdapter.java new file mode 100644 index 0000000..c7705ea --- /dev/null +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/mount/BrowsingAutocompleteAdapter.java @@ -0,0 +1,135 @@ +/* + * 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 . + */ + +package com.google.android.sambadocumentsprovider.mount; + +import android.widget.Filter; +import android.widget.Filterable; + +import com.google.android.sambadocumentsprovider.R; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class BrowsingAutocompleteAdapter extends SectionedAdapter implements Filterable { + private final List mServers = new ArrayList<>(); + + private List mFilteredServers = new ArrayList<>(); + + public void addServer(String name, List children) { + synchronized (mServers) { + mServers.add(new ServerInfo(name, children)); + } + + notifyDataSetChanged(); + } + + @Override + public Filter getFilter() { + return new Filter() { + @Override + protected FilterResults performFiltering(CharSequence charSequence) { + String query = charSequence == null ? "" : charSequence.toString().toLowerCase(); + + List filteredServers = new ArrayList<>(); + + if (!query.isEmpty()) { + filteredServers.add(new ServerInfo(query, Collections.emptyList())); + } + + if (!isLoadingData()) { + synchronized (mServers) { + for (ServerInfo serverInfo : mServers) { + + List displayedShares = new ArrayList<>(); + for (String share : serverInfo.getChildren()) { + if (share.toLowerCase().contains(query)) { + displayedShares.add(share); + } + } + + if (!displayedShares.isEmpty()) { + filteredServers.add(new ServerInfo(serverInfo.getName(), displayedShares)); + } + } + } + } + + FilterResults results = new FilterResults(); + results.values = filteredServers; + + return results; + } + + @Override + protected void publishResults(CharSequence charSequence, FilterResults filterResults) { + mFilteredServers = (List) filterResults.values; + + notifyDataSetChanged(); + } + }; + } + + @Override + public int getSectionCount() { + return mFilteredServers.size(); + } + + @Override + public String getNameForSection(int sectionIndex) { + return mFilteredServers.get(sectionIndex).getName(); + } + + @Override + public int getIconForSection(int sectionIndex) { + return R.drawable.ic_server; + } + + @Override + public int getSizeForSection(int sectionIndex) { + return mFilteredServers.get(sectionIndex).getChildren().size(); + } + + @Override + public String getNameForRow(int sectionIndex, int rowIndex) { + return mFilteredServers.get(sectionIndex).getChildren().get(rowIndex); + } + + @Override + public int getIconForRow(int sectionIndex, int rowIndex) { + return R.drawable.ic_folder_shared; + } + + private static class ServerInfo { + private final String mName; + private final List mChildren; + + ServerInfo(String name, List children) { + this.mName = name; + this.mChildren = children; + } + + String getName() { + return mName; + } + + List getChildren() { + return mChildren; + } + } +} diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/mount/BrowsingAutocompleteTextView.java b/app/src/main/java/com/google/android/sambadocumentsprovider/mount/BrowsingAutocompleteTextView.java new file mode 100644 index 0000000..ddee321 --- /dev/null +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/mount/BrowsingAutocompleteTextView.java @@ -0,0 +1,45 @@ +/* + * 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 . + */ + +package com.google.android.sambadocumentsprovider.mount; + +import android.content.Context; +import android.util.AttributeSet; + +public class BrowsingAutocompleteTextView extends android.support.v7.widget.AppCompatAutoCompleteTextView { + public BrowsingAutocompleteTextView(Context context) { + super(context); + } + + public BrowsingAutocompleteTextView(Context context, AttributeSet attributeSet) { + super(context, attributeSet); + } + + public BrowsingAutocompleteTextView(Context context, AttributeSet attributeSet, int v) { + super(context, attributeSet, v); + } + + @Override + public boolean enoughToFilter() { + return true; + } + + public void filter() { + int length = getText().length(); + performFiltering(getText(), length == 0 ? 0 : getText().charAt(length - 1)); + } +} diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/mount/MountServerActivity.java b/app/src/main/java/com/google/android/sambadocumentsprovider/mount/MountServerActivity.java index 58726ea..faccc5c 100644 --- a/app/src/main/java/com/google/android/sambadocumentsprovider/mount/MountServerActivity.java +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/mount/MountServerActivity.java @@ -38,6 +38,7 @@ import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnKeyListener; @@ -52,19 +53,20 @@ import com.google.android.sambadocumentsprovider.ShareManager; import com.google.android.sambadocumentsprovider.TaskManager; import com.google.android.sambadocumentsprovider.base.AuthFailedException; import com.google.android.sambadocumentsprovider.base.OnTaskFinishedCallback; +import com.google.android.sambadocumentsprovider.browsing.NetworkBrowser; import com.google.android.sambadocumentsprovider.cache.DocumentCache; import com.google.android.sambadocumentsprovider.document.DocumentMetadata; import com.google.android.sambadocumentsprovider.nativefacade.SmbClient; import com.google.android.sambadocumentsprovider.provider.SambaDocumentsProvider; import java.util.List; +import java.util.Map; public class MountServerActivity extends AppCompatActivity { private static final String TAG = "MountServerActivity"; private static final String ACTION_BROWSE = "android.provider.action.BROWSE"; - private static final String SHARE_PATH_KEY = "sharePath"; private static final String NEEDS_PASSWORD_KEY = "needsPassword"; private static final String DOMAIN_KEY = "domain"; @@ -100,15 +102,34 @@ public class MountServerActivity extends AppCompatActivity { } }; + private final OnTaskFinishedCallback>> mBrowsingCallback + = new OnTaskFinishedCallback>>() { + @Override + public void onTaskFinished( + @Status int status, @Nullable Map> result, @Nullable Exception exception) { + + for (String server : result.keySet()) { + mBrowsingAdapter.addServer(server, result.get(server)); + } + + mBrowsingAdapter.finishLoading(); + + if (mSharePathEditText.isPopupShowing()) { + mSharePathEditText.filter(); + } + } + }; + private DocumentCache mCache; private TaskManager mTaskManager; private ShareManager mShareManager; private SmbClient mClient; + private BrowsingAutocompleteAdapter mBrowsingAdapter; private CheckBox mNeedPasswordCheckbox; private View mPasswordHideGroup; - private EditText mSharePathEditText; + private BrowsingAutocompleteTextView mSharePathEditText; private EditText mDomainEditText; private EditText mUsernameEditText; private EditText mPasswordEditText; @@ -130,7 +151,7 @@ public class MountServerActivity extends AppCompatActivity { mPasswordHideGroup = findViewById(R.id.password_hide_group); - mSharePathEditText = (EditText) findViewById(R.id.share_path); + mSharePathEditText = (BrowsingAutocompleteTextView) findViewById(R.id.share_path); mSharePathEditText.setOnKeyListener(mMountKeyListener); mUsernameEditText = (EditText) findViewById(R.id.username); @@ -158,6 +179,8 @@ public class MountServerActivity extends AppCompatActivity { mConnectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); restoreSavedInstanceState(savedInstanceState); + + startBrowsing(); } private void restoreSavedInstanceState(@Nullable Bundle savedInstanceState) { @@ -220,6 +243,23 @@ public class MountServerActivity extends AppCompatActivity { } } + private void startBrowsing() { + mSharePathEditText.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + mSharePathEditText.filter(); + return false; + } + }); + + mBrowsingAdapter = new BrowsingAutocompleteAdapter(); + mSharePathEditText.setAdapter(mBrowsingAdapter); + mSharePathEditText.setThreshold(0); + + NetworkBrowser browser = new NetworkBrowser(mClient, mTaskManager); + browser.getSharesAsync(mBrowsingCallback); + } + private void tryMount() { final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); if (info == null || !info.isConnected()) { diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/mount/SectionedAdapter.java b/app/src/main/java/com/google/android/sambadocumentsprovider/mount/SectionedAdapter.java new file mode 100644 index 0000000..ca0523a --- /dev/null +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/mount/SectionedAdapter.java @@ -0,0 +1,246 @@ +/* + * 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 . + */ + +package com.google.android.sambadocumentsprovider.mount; + +import android.graphics.Typeface; +import android.support.annotation.IntDef; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.google.android.sambadocumentsprovider.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +public abstract class SectionedAdapter extends BaseAdapter { + private static final String TAG = "SectionedAdapter"; + + @IntDef({SECTION_HEADER, REGULAR_ROW, LOADING_ROW}) + @Retention(RetentionPolicy.SOURCE) + private @interface Type {} + private static final int SECTION_HEADER = 0; + private static final int REGULAR_ROW = 1; + private static final int LOADING_ROW = 2; + + private List mFlatRows = new ArrayList<>(); + + private volatile boolean mIsLoadingData = true; + + public void finishLoading() { + mIsLoadingData = false; + } + + public abstract int getSectionCount(); + + public abstract String getNameForSection(int sectionIndex); + public abstract int getIconForSection(int sectionIndex); + public abstract int getSizeForSection(int sectionIndex); + + public abstract String getNameForRow(int sectionIndex, int rowIndex); + public abstract int getIconForRow(int sectionIndex, int rowIndex); + + @Override + public int getCount() { + return mFlatRows.size() + (mIsLoadingData ? 1 : 0); + } + + @Override + public FlatRow getItem(int position) { + if (position == mFlatRows.size() && mIsLoadingData) { + return null; + } + + return mFlatRows.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + public @Type int getItemViewType(int position) { + if (position == mFlatRows.size()) { + return LOADING_ROW; + } + + return mFlatRows.get(position).isSectionHeader() ? SECTION_HEADER : REGULAR_ROW; + } + + @Override + public View getView(int position, View reuseView, ViewGroup parent) { + int viewType = getItemViewType(position); + + if (reuseView == null) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + + switch (viewType) { + case LOADING_ROW: + reuseView = inflater.inflate(R.layout.browsing_progress_item_layout, null); + break; + case SECTION_HEADER: + case REGULAR_ROW: + reuseView = inflater.inflate(R.layout.browsing_list_item_layout, null); + break; + default: + // Should never happen. + throw new RuntimeException("Unknown view type: " + viewType); + } + + reuseView.setTag(new ViewHolder(reuseView)); + } + + ViewHolder holder = (ViewHolder) reuseView.getTag(); + + if (viewType == LOADING_ROW) { + return reuseView; + } + + holder.bindView(mFlatRows.get(position)); + + return reuseView; + } + + @Override + public boolean isEnabled(int position) { + if (position == mFlatRows.size() && mIsLoadingData) { + return false; + } + + return !mFlatRows.get(position).isSectionHeader(); + } + + @Override + public void notifyDataSetChanged() { + flattenData(); + + super.notifyDataSetChanged(); + } + + private void flattenData() { + mFlatRows.clear(); + + int rowsCount = 0; + + for (int i = 0; i < getSectionCount(); i++) { + rowsCount = addFlatRow(getNameForSection(i), getIconForSection(i), true, rowsCount); + + for (int j = 0; j < getSizeForSection(i); j++) { + rowsCount = addFlatRow(getNameForRow(i, j), getIconForRow(i, j), false, rowsCount); + } + } + } + + private int addFlatRow(String name, int iconId, boolean isSectionHeader, int position) { + FlatRow newRow = FlatRow.create( + name, + iconId, + isSectionHeader, + position < mFlatRows.size() ? mFlatRows.get(position) : null); + + + if (position < mFlatRows.size()) { + mFlatRows.set(position, newRow); + } else { + mFlatRows.add(newRow); + } + + return position + 1; + } + + protected boolean isLoadingData() { + return mIsLoadingData; + } + + private static class ViewHolder { + private ImageView mIconImageView; + private TextView mNameTextView; + + ViewHolder(View parent) { + mIconImageView = parent.findViewById(R.id.browsing_icon); + mNameTextView = parent.findViewById(R.id.browsing_name); + } + + void bindView(FlatRow row) { + if (mIconImageView == null || mNameTextView == null) { + return; + } + + if (row.isSectionHeader()) { + mIconImageView.setPadding(8, 0, 0, 0); + } + + mIconImageView.setImageResource(row.getIconId()); + mNameTextView.setText(row.getName()); + + if (row.isSectionHeader()) { + mIconImageView.setPadding(0, 0, 0, 0); + mNameTextView.setTypeface(null, Typeface.BOLD); + } else { + mIconImageView.setPadding(16, 0, 0, 0); + mNameTextView.setTypeface(null, Typeface.NORMAL); + } + } + } + + private static class FlatRow { + private String mName; + private int mIconId; + private boolean mIsSectionHeader; + + private FlatRow(String name, int iconId, boolean isSectionHeader) { + mName = name; + mIconId = iconId; + mIsSectionHeader = isSectionHeader; + } + + static FlatRow create(String name, int iconId, boolean isSectionHeader, FlatRow reuse) { + if (reuse == null) { + return new FlatRow(name, iconId, isSectionHeader); + } + + reuse.mName = name; + reuse.mIconId = iconId; + reuse.mIsSectionHeader = isSectionHeader; + + return reuse; + } + + String getName() { + return mName; + } + + int getIconId() { + return mIconId; + } + + boolean isSectionHeader() { + return mIsSectionHeader; + } + + @Override + public String toString() { + return mName; + } + } +} diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/provider/SambaDocumentsProvider.java b/app/src/main/java/com/google/android/sambadocumentsprovider/provider/SambaDocumentsProvider.java index 3fc1774..072a112 100644 --- a/app/src/main/java/com/google/android/sambadocumentsprovider/provider/SambaDocumentsProvider.java +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/provider/SambaDocumentsProvider.java @@ -28,7 +28,6 @@ import android.content.Context; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; @@ -59,7 +58,6 @@ import com.google.android.sambadocumentsprovider.document.LoadChildrenTask; import com.google.android.sambadocumentsprovider.base.OnTaskFinishedCallback; import com.google.android.sambadocumentsprovider.document.LoadDocumentTask; import com.google.android.sambadocumentsprovider.document.LoadStatTask; -import com.google.android.sambadocumentsprovider.mount.MountServerActivity; import com.google.android.sambadocumentsprovider.nativefacade.SmbFacade; import java.io.FileNotFoundException; @@ -131,22 +129,6 @@ public class SambaDocumentsProvider extends DocumentsProvider { } }; - private final OnTaskFinishedCallback> mLoadSharesFinishedCallback = - new OnTaskFinishedCallback>() { - @Override - public void onTaskFinished( - @OnTaskFinishedCallback.Status int status, - @Nullable List 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() { @Override public void onMountedServerChange() { @@ -162,7 +144,6 @@ public class SambaDocumentsProvider extends DocumentsProvider { private DocumentCache mCache; private TaskManager mTaskManager; private StorageManager mStorageManager; - private NetworkBrowser mNetworkBrowser; private List mBrowsingStorage; @@ -178,7 +159,6 @@ public class SambaDocumentsProvider extends DocumentsProvider { mShareManager = SambaProviderApplication.getServerManager(context); mShareManager.addListener(mShareChangeListener); mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); - mNetworkBrowser = SambaProviderApplication.getNetworkBrowser(context); return mClient != null; } @@ -190,14 +170,6 @@ public class SambaDocumentsProvider extends DocumentsProvider { MatrixCursor cursor = new MatrixCursor(projection); - 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) { if (!mShareManager.isShareMounted(uri)) { continue; @@ -253,12 +225,6 @@ public class SambaDocumentsProvider extends DocumentsProvider { final MatrixCursor cursor = new MatrixCursor(projection); final Uri uri = toUri(documentId); - if (documentId.equals(NetworkBrowser.SMB_BROWSING_URI.toString())) { - cursor.addRow(getCursorRowForBrowsingRoot(projection)); - - return cursor; - } - try { try (CacheResult result = mCache.get(uri)) { @@ -292,10 +258,6 @@ public class SambaDocumentsProvider extends DocumentsProvider { if (BuildConfig.DEBUG) Log.d(TAG, "Querying children documents under " + documentId); projection = (projection == null) ? DEFAULT_DOCUMENT_PROJECTION : projection; - if (documentId.equals(NetworkBrowser.SMB_BROWSING_URI.toString())) { - return getFilesSharesCursor(projection); - } - final Uri uri = toUri(documentId); try { @@ -439,68 +401,6 @@ public class SambaDocumentsProvider extends DocumentsProvider { 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 == null) { - 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 = null; - } - - return cursor; - } - @Override public String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException { diff --git a/app/src/main/res/drawable/ic_cloud.xml b/app/src/main/res/drawable/ic_keyboard.xml similarity index 71% rename from app/src/main/res/drawable/ic_cloud.xml rename to app/src/main/res/drawable/ic_keyboard.xml index e7a8fed..5365fba 100644 --- a/app/src/main/res/drawable/ic_cloud.xml +++ b/app/src/main/res/drawable/ic_keyboard.xml @@ -21,8 +21,13 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - - \ No newline at end of file + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 901fa95..f4880c6 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -40,7 +40,7 @@ android:layout_height="wrap_content" android:orientation="vertical"> - diff --git a/app/src/main/res/layout/browsing_list_item_layout.xml b/app/src/main/res/layout/browsing_list_item_layout.xml new file mode 100644 index 0000000..9dcb0b6 --- /dev/null +++ b/app/src/main/res/layout/browsing_list_item_layout.xml @@ -0,0 +1,42 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/browsing_progress_item_layout.xml b/app/src/main/res/layout/browsing_progress_item_layout.xml new file mode 100644 index 0000000..da8d5ca --- /dev/null +++ b/app/src/main/res/layout/browsing_progress_item_layout.xml @@ -0,0 +1,36 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7908a54..86bc0e0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -49,6 +49,7 @@ It needs a web browser to send feedback. Samba Shares + Looking for shares… Pin this share Login