Merge pull request #31 from rthakohov/authentication
Authentication on demand
This commit is contained in:
commit
cde6c2ae83
11 changed files with 407 additions and 62 deletions
|
@ -48,6 +48,7 @@
|
|||
</intent-filter>
|
||||
</provider>
|
||||
|
||||
<activity android:name=".auth.AuthActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -45,6 +45,7 @@ public class ShareManager implements Iterable<String> {
|
|||
|
||||
// JSON value
|
||||
private static final String URI_KEY = "uri";
|
||||
private static final String MOUNT_KEY = "mount";
|
||||
private static final String CREDENTIAL_TUPLE_KEY = "credentialTuple";
|
||||
private static final String WORKGROUP_KEY = "workgroup";
|
||||
private static final String USERNAME_KEY = "username";
|
||||
|
@ -52,6 +53,7 @@ public class ShareManager implements Iterable<String> {
|
|||
|
||||
private final SharedPreferences mPref;
|
||||
private final Set<String> mServerStringSet;
|
||||
private final Set<String> mMountedServerSet = new HashSet<>();
|
||||
private final Map<String, String> mServerStringMap = new HashMap<>();
|
||||
private final CredentialCache mCredentialCache;
|
||||
|
||||
|
@ -61,13 +63,13 @@ public class ShareManager implements Iterable<String> {
|
|||
mCredentialCache = credentialCache;
|
||||
|
||||
mPref = context.getSharedPreferences(SERVER_CACHE_PREF_KEY, Context.MODE_PRIVATE);
|
||||
// Loading mounted servers
|
||||
// Loading saved servers.
|
||||
final Set<String> serverStringSet =
|
||||
mPref.getStringSet(SERVER_STRING_SET_KEY, Collections.<String> emptySet());
|
||||
final Map<String, CredentialTuple> credentialMap = new HashMap<>(serverStringSet.size());
|
||||
final Map<String, ShareTuple> shareMap = new HashMap<>(serverStringSet.size());
|
||||
for (String serverString : serverStringSet) {
|
||||
// TODO: Add decryption
|
||||
String uri = decode(serverString, credentialMap);
|
||||
String uri = decode(serverString, shareMap);
|
||||
if (uri != null) {
|
||||
mServerStringMap.put(uri, serverString);
|
||||
}
|
||||
|
@ -75,24 +77,89 @@ public class ShareManager implements Iterable<String> {
|
|||
|
||||
mServerStringSet = new HashSet<>(serverStringSet);
|
||||
|
||||
for (Map.Entry<String, CredentialTuple> server : credentialMap.entrySet()) {
|
||||
final CredentialTuple tuple = server.getValue();
|
||||
for (Map.Entry<String, ShareTuple> server : shareMap.entrySet()) {
|
||||
final ShareTuple tuple = server.getValue();
|
||||
|
||||
if (tuple.mIsMounted) {
|
||||
mMountedServerSet.add(server.getKey());
|
||||
}
|
||||
|
||||
mCredentialCache.putCredential(
|
||||
server.getKey(), tuple.mWorkgroup, tuple.mUsername, tuple.mPassword);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void mountServer(
|
||||
String uri, String workgroup, String username, String password, ShareMountChecker checker)
|
||||
throws IOException {
|
||||
if (mServerStringMap.containsKey(uri)) {
|
||||
throw new IllegalStateException("Uri " + uri + " is already mounted.");
|
||||
/**
|
||||
* Save the server and credentials to permanent storage.
|
||||
* Throw an exception if a server with such a uri is already present.
|
||||
*/
|
||||
public synchronized void addServer(
|
||||
String uri, String workgroup, String username, String password,
|
||||
ShareMountChecker checker, boolean mount) throws IOException {
|
||||
|
||||
if (mMountedServerSet.contains(uri)) {
|
||||
throw new IllegalStateException("Uri " + uri + " is already stored.");
|
||||
}
|
||||
|
||||
saveServerInfo(uri, workgroup, username, password, checker, mount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the server info. If a server with such a uri doesn't exist, create it.
|
||||
*/
|
||||
public synchronized void addOrUpdateServer(
|
||||
String uri, String workgroup, String username, String password,
|
||||
ShareMountChecker checker, boolean mount) throws IOException {
|
||||
saveServerInfo(uri, workgroup, username, password, checker, mount);
|
||||
}
|
||||
|
||||
private void saveServerInfo(
|
||||
String uri, String workgroup, String username, String password,
|
||||
ShareMountChecker checker, boolean mount) throws IOException {
|
||||
|
||||
checkServerCredentials(uri, workgroup, username, password, checker);
|
||||
|
||||
final boolean hasPassword = !TextUtils.isEmpty(username) && !TextUtils.isEmpty(password);
|
||||
if (hasPassword) {
|
||||
final ShareTuple tuple = hasPassword
|
||||
? new ShareTuple(workgroup, username, password, mount)
|
||||
: ShareTuple.EMPTY_TUPLE;
|
||||
|
||||
updateServersData(uri, tuple, mount);
|
||||
}
|
||||
|
||||
private void updateServersData(
|
||||
String uri, ShareTuple tuple, boolean shouldNotify) {
|
||||
final String serverString = encode(uri, tuple);
|
||||
if (serverString == null) {
|
||||
throw new IllegalStateException("Failed to encode credential tuple.");
|
||||
}
|
||||
// TODO: Add encryption
|
||||
mServerStringSet.add(serverString);
|
||||
if (tuple.mIsMounted) {
|
||||
mMountedServerSet.add(uri);
|
||||
} else {
|
||||
mMountedServerSet.remove(uri);
|
||||
}
|
||||
mPref.edit().putStringSet(SERVER_STRING_SET_KEY, mServerStringSet).apply();
|
||||
mServerStringMap.put(uri, serverString);
|
||||
|
||||
if (shouldNotify) {
|
||||
notifyServerChange();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkServerCredentials(
|
||||
String uri, String workgroup, String username, String password, ShareMountChecker checker)
|
||||
throws IOException {
|
||||
|
||||
if (!username.isEmpty() && !password.isEmpty()) {
|
||||
mCredentialCache.putCredential(uri, workgroup, username, password);
|
||||
}
|
||||
|
||||
runMountChecker(uri, checker);
|
||||
}
|
||||
|
||||
private void runMountChecker(String uri, ShareMountChecker checker) throws IOException {
|
||||
try {
|
||||
checker.checkShareMounting();
|
||||
} catch (Exception e) {
|
||||
|
@ -100,20 +167,6 @@ public class ShareManager implements Iterable<String> {
|
|||
mCredentialCache.removeCredential(uri);
|
||||
throw e;
|
||||
}
|
||||
|
||||
final CredentialTuple tuple = hasPassword
|
||||
? new CredentialTuple(workgroup, username, password)
|
||||
: CredentialTuple.EMPTY_TUPLE;
|
||||
final String serverString = encode(uri, tuple);
|
||||
if (serverString == null) {
|
||||
throw new IllegalStateException("Failed to encode credential tuple.");
|
||||
}
|
||||
// TODO: Add encryption
|
||||
mServerStringSet.add(serverString);
|
||||
mPref.edit().putStringSet(SERVER_STRING_SET_KEY, mServerStringSet).apply();
|
||||
|
||||
mServerStringMap.put(uri, serverString);
|
||||
notifyServerChange();
|
||||
}
|
||||
|
||||
public synchronized boolean unmountServer(String uri) {
|
||||
|
@ -149,6 +202,10 @@ public class ShareManager implements Iterable<String> {
|
|||
return mServerStringMap.containsKey(uri);
|
||||
}
|
||||
|
||||
public synchronized boolean isShareMounted(String uri) {
|
||||
return mMountedServerSet.contains(uri);
|
||||
}
|
||||
|
||||
private void notifyServerChange() {
|
||||
for (int i = mListeners.size() - 1; i >= 0; --i) {
|
||||
mListeners.get(i).onMountedServerChange();
|
||||
|
@ -163,7 +220,7 @@ public class ShareManager implements Iterable<String> {
|
|||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
private static String encode(String uri, CredentialTuple tuple) {
|
||||
private static String encode(String uri, ShareTuple tuple) {
|
||||
final StringWriter stringWriter = new StringWriter();
|
||||
try (final JsonWriter jsonWriter = new JsonWriter(stringWriter)) {
|
||||
jsonWriter.beginObject();
|
||||
|
@ -180,25 +237,26 @@ public class ShareManager implements Iterable<String> {
|
|||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
private static void encodeTuple(JsonWriter writer, CredentialTuple tuple) throws IOException {
|
||||
if (tuple == CredentialTuple.EMPTY_TUPLE) {
|
||||
private static void encodeTuple(JsonWriter writer, ShareTuple tuple) throws IOException {
|
||||
if (tuple == ShareTuple.EMPTY_TUPLE) {
|
||||
writer.nullValue();
|
||||
} else {
|
||||
writer.beginObject();
|
||||
writer.name(WORKGROUP_KEY).value(tuple.mWorkgroup);
|
||||
writer.name(USERNAME_KEY).value(tuple.mUsername);
|
||||
writer.name(PASSWORD_KEY).value(tuple.mPassword);
|
||||
writer.name(MOUNT_KEY).value(tuple.mIsMounted);
|
||||
writer.endObject();
|
||||
}
|
||||
}
|
||||
|
||||
private static String decode(String content, Map<String, CredentialTuple> credentialMap) {
|
||||
private static String decode(String content, Map<String, ShareTuple> shareMap) {
|
||||
final StringReader stringReader = new StringReader(content);
|
||||
try (final JsonReader jsonReader = new JsonReader(stringReader)) {
|
||||
jsonReader.beginObject();
|
||||
|
||||
String uri = null;
|
||||
CredentialTuple tuple = null;
|
||||
ShareTuple tuple = null;
|
||||
while (jsonReader.hasNext()) {
|
||||
final String name = jsonReader.nextName();
|
||||
switch (name) {
|
||||
|
@ -217,7 +275,7 @@ public class ShareManager implements Iterable<String> {
|
|||
if (uri == null || tuple == null) {
|
||||
throw new IllegalStateException("Either uri or tuple is null.");
|
||||
}
|
||||
credentialMap.put(uri, tuple);
|
||||
shareMap.put(uri, tuple);
|
||||
|
||||
return uri;
|
||||
} catch (IOException e) {
|
||||
|
@ -226,48 +284,51 @@ public class ShareManager implements Iterable<String> {
|
|||
}
|
||||
}
|
||||
|
||||
private static CredentialTuple decodeTuple(JsonReader reader) throws IOException {
|
||||
private static ShareTuple decodeTuple(JsonReader reader) throws IOException {
|
||||
if (reader.peek() == JsonToken.NULL) {
|
||||
reader.nextNull();
|
||||
return CredentialTuple.EMPTY_TUPLE;
|
||||
return ShareTuple.EMPTY_TUPLE;
|
||||
}
|
||||
|
||||
String workgroup = null;
|
||||
String username = null;
|
||||
String password = null;
|
||||
boolean mounted = true;
|
||||
|
||||
reader.beginObject();
|
||||
while (reader.hasNext()) {
|
||||
String name = reader.nextName();
|
||||
String value = reader.nextString();
|
||||
|
||||
switch (name) {
|
||||
case WORKGROUP_KEY:
|
||||
workgroup = value;
|
||||
workgroup = reader.nextString();
|
||||
break;
|
||||
case USERNAME_KEY:
|
||||
username = value;
|
||||
username = reader.nextString();
|
||||
break;
|
||||
case PASSWORD_KEY:
|
||||
password = value;
|
||||
password = reader.nextString();
|
||||
break;
|
||||
case MOUNT_KEY:
|
||||
mounted = reader.nextBoolean();
|
||||
default:
|
||||
Log.w(TAG, "Ignoring unknown key " + name);
|
||||
}
|
||||
}
|
||||
reader.endObject();
|
||||
|
||||
return new CredentialTuple(workgroup, username, password);
|
||||
return new ShareTuple(workgroup, username, password, mounted);
|
||||
}
|
||||
|
||||
private static class CredentialTuple {
|
||||
private static final CredentialTuple EMPTY_TUPLE = new CredentialTuple("", "", "");
|
||||
private static class ShareTuple {
|
||||
private static final ShareTuple EMPTY_TUPLE = new ShareTuple("", "", "", true);
|
||||
|
||||
private final String mWorkgroup;
|
||||
private final String mUsername;
|
||||
private final String mPassword;
|
||||
private boolean mIsMounted;
|
||||
|
||||
private CredentialTuple(String workgroup, String username, String password) {
|
||||
private ShareTuple(String workgroup, String username, String password, boolean isMounted) {
|
||||
if (workgroup == null) {
|
||||
throw new IllegalArgumentException("workgroup is null.");
|
||||
}
|
||||
|
@ -280,6 +341,7 @@ public class ShareManager implements Iterable<String> {
|
|||
mWorkgroup = workgroup;
|
||||
mUsername = username;
|
||||
mPassword = password;
|
||||
mIsMounted = isMounted;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* 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.auth;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.sambadocumentsprovider.R;
|
||||
import com.google.android.sambadocumentsprovider.SambaProviderApplication;
|
||||
import com.google.android.sambadocumentsprovider.ShareManager;
|
||||
import com.google.android.sambadocumentsprovider.base.AuthFailedException;
|
||||
import com.google.android.sambadocumentsprovider.base.OnTaskFinishedCallback;
|
||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
||||
|
||||
public class AuthActivity extends AppCompatActivity {
|
||||
private static final String TAG = "AuthActivity";
|
||||
|
||||
private static final String SHARE_URI_KEY = "shareUri";
|
||||
|
||||
private EditText mSharePathEditText;
|
||||
private EditText mDomainEditText;
|
||||
private EditText mUsernameEditText;
|
||||
private EditText mPasswordEditText;
|
||||
|
||||
private CheckBox mPinShareCheckbox;
|
||||
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
private ShareManager mShareManager;
|
||||
private SmbClient mClient;
|
||||
|
||||
private final View.OnClickListener mLoginListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
tryAuth();
|
||||
}
|
||||
};
|
||||
|
||||
private final OnTaskFinishedCallback<Void> callback = new OnTaskFinishedCallback<Void>() {
|
||||
@Override
|
||||
public void onTaskFinished(@Status int status, @Nullable Void item, @Nullable Exception e) {
|
||||
progressDialog.dismiss();
|
||||
|
||||
if (status == SUCCEEDED) {
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
} else {
|
||||
Log.i(TAG, "Authentication failed: ", e);
|
||||
|
||||
showMessage(R.string.credential_error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
final Context context = getApplicationContext();
|
||||
mShareManager = SambaProviderApplication.getServerManager(context);
|
||||
mClient = SambaProviderApplication.getSambaClient(context);
|
||||
|
||||
Intent authIntent = getIntent();
|
||||
String shareUri = authIntent.getStringExtra(SHARE_URI_KEY);
|
||||
|
||||
prepareUI(shareUri);
|
||||
}
|
||||
|
||||
private void prepareUI(String shareUri) {
|
||||
mSharePathEditText = (EditText) findViewById(R.id.share_path);
|
||||
mUsernameEditText = (EditText) findViewById(R.id.username);
|
||||
mDomainEditText = (EditText) findViewById(R.id.domain);
|
||||
mPasswordEditText = (EditText) findViewById(R.id.password);
|
||||
|
||||
CheckBox passwordCheckbox = (CheckBox) findViewById(R.id.needs_password);
|
||||
mPinShareCheckbox = (CheckBox) findViewById(R.id.pin_share);
|
||||
|
||||
mSharePathEditText.setText(shareUri);
|
||||
mSharePathEditText.setEnabled(false);
|
||||
|
||||
passwordCheckbox.setVisibility(View.GONE);
|
||||
mPinShareCheckbox.setVisibility(View.VISIBLE);
|
||||
|
||||
Button mLoginButton = (Button) findViewById(R.id.mount);
|
||||
mLoginButton.setText(getResources().getString(R.string.login));
|
||||
mLoginButton.setOnClickListener(mLoginListener);
|
||||
|
||||
final Button cancel = (Button) findViewById(R.id.cancel);
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void tryAuth() {
|
||||
progressDialog = ProgressDialog.show(
|
||||
this, null, getResources().getString(R.string.authenticating), true);
|
||||
|
||||
final String username = mUsernameEditText.getText().toString();
|
||||
final String password = mPasswordEditText.getText().toString();
|
||||
|
||||
if (username.isEmpty() || password.isEmpty()) {
|
||||
showMessage(R.string.empty_credentials);
|
||||
return;
|
||||
}
|
||||
|
||||
new AuthorizationTask(
|
||||
mSharePathEditText.getText().toString(),
|
||||
username,
|
||||
password,
|
||||
mDomainEditText.getText().toString(),
|
||||
mPinShareCheckbox.isChecked(),
|
||||
mShareManager,
|
||||
mClient,
|
||||
callback).execute();
|
||||
}
|
||||
|
||||
private void showMessage(@StringRes int id) {
|
||||
Snackbar.make(mPinShareCheckbox, id, Snackbar.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public static PendingIntent createAuthIntent(Context context, String shareUri) {
|
||||
Intent authIntent = new Intent();
|
||||
authIntent.setComponent(new ComponentName(
|
||||
context.getPackageName(),
|
||||
AuthActivity.class.getName()));
|
||||
authIntent.putExtra(SHARE_URI_KEY, shareUri);
|
||||
|
||||
return PendingIntent.getActivity(
|
||||
context, 0, authIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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.auth;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.android.sambadocumentsprovider.ShareManager;
|
||||
import com.google.android.sambadocumentsprovider.base.BiResultTask;
|
||||
import com.google.android.sambadocumentsprovider.base.OnTaskFinishedCallback;
|
||||
import com.google.android.sambadocumentsprovider.document.DocumentMetadata;
|
||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class AuthorizationTask extends BiResultTask<Void, Void, Void> {
|
||||
private static final String TAG = "AuthorizationTask";
|
||||
|
||||
private final String mUri;
|
||||
private final String mUser;
|
||||
private final String mPassword;
|
||||
private final String mDomain;
|
||||
|
||||
private final boolean mShouldPin;
|
||||
private final ShareManager mShareManager;
|
||||
private final SmbClient mClient;
|
||||
private final OnTaskFinishedCallback<Void> mCallback;
|
||||
|
||||
AuthorizationTask(String uri, String user, String password, String domain, boolean shouldPin,
|
||||
ShareManager shareManager, SmbClient client,
|
||||
OnTaskFinishedCallback<Void> callback) {
|
||||
mUri = uri;
|
||||
mUser = user;
|
||||
mPassword = password;
|
||||
mDomain = domain;
|
||||
|
||||
mShouldPin = shouldPin;
|
||||
mShareManager = shareManager;
|
||||
mClient = client;
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void run(Void... voids) throws Exception {
|
||||
final DocumentMetadata shareMetadata = DocumentMetadata.createShare(Uri.parse(mUri));
|
||||
|
||||
final ShareManager.ShareMountChecker mountChecker = new ShareManager.ShareMountChecker() {
|
||||
@Override
|
||||
public void checkShareMounting() throws IOException {
|
||||
shareMetadata.loadChildren(mClient);
|
||||
}
|
||||
};
|
||||
|
||||
mShareManager.addOrUpdateServer(mUri, mDomain, mUser, mPassword, mountChecker, mShouldPin);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSucceeded(Void aVoid) {
|
||||
mCallback.onTaskFinished(OnTaskFinishedCallback.SUCCEEDED, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(Exception e) {
|
||||
mCallback.onTaskFinished(OnTaskFinishedCallback.FAILED, null, e);
|
||||
}
|
||||
}
|
|
@ -137,7 +137,7 @@ class BroadcastUtils {
|
|||
final int type = buffer.get();
|
||||
|
||||
if (type == FILE_SERVER_NODE_TYPE) {
|
||||
servers.add(serverName);
|
||||
servers.add(serverName.trim());
|
||||
}
|
||||
|
||||
skipBytes(buffer, 2);
|
||||
|
|
|
@ -310,6 +310,10 @@ public class DocumentMetadata {
|
|||
return uri.getPathSegments().isEmpty() && !uri.getAuthority().isEmpty();
|
||||
}
|
||||
|
||||
public static boolean isShareUri(Uri uri) {
|
||||
return uri.getPathSegments().size() == 1;
|
||||
}
|
||||
|
||||
public static DocumentMetadata fromUri(Uri uri, SmbClient client) throws IOException {
|
||||
final List<String> pathSegments = uri.getPathSegments();
|
||||
if (pathSegments.isEmpty()) {
|
||||
|
|
|
@ -70,6 +70,8 @@ public class MountServerActivity extends AppCompatActivity {
|
|||
private static final String DOMAIN_KEY = "domain";
|
||||
private static final String USERNAME_KEY = "username";
|
||||
private static final String PASSWORD_KEY = "password";
|
||||
private static final String AUTH_LAUNCH_KEY = "authLaunch";
|
||||
|
||||
|
||||
private final OnClickListener mPasswordStateChangeListener = new OnClickListener() {
|
||||
@Override
|
||||
|
@ -136,8 +138,8 @@ public class MountServerActivity extends AppCompatActivity {
|
|||
mPasswordEditText = (EditText) findViewById(R.id.password);
|
||||
mPasswordEditText.setOnKeyListener(mMountKeyListener);
|
||||
|
||||
final Button mount = (Button) findViewById(R.id.mount);
|
||||
mount.setOnClickListener(mMountListener);
|
||||
final Button mMountShareButton = (Button) findViewById(R.id.mount);
|
||||
mMountShareButton.setOnClickListener(mMountListener);
|
||||
|
||||
final Button cancel = (Button) findViewById(R.id.cancel);
|
||||
cancel.setOnClickListener(new OnClickListener() {
|
||||
|
@ -147,6 +149,8 @@ public class MountServerActivity extends AppCompatActivity {
|
|||
}
|
||||
});
|
||||
|
||||
setNeedsPasswordState(false);
|
||||
|
||||
// Set MovementMethod to make it respond to clicks on hyperlinks
|
||||
final TextView gplv3Link = (TextView) findViewById(R.id.gplv3_link);
|
||||
gplv3Link.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
@ -162,6 +166,7 @@ public class MountServerActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
mSharePathEditText.setText(savedInstanceState.getString(SHARE_PATH_KEY, ""));
|
||||
|
||||
final boolean needsPassword = savedInstanceState.getBoolean(NEEDS_PASSWORD_KEY);
|
||||
setNeedsPasswordState(needsPassword);
|
||||
if (needsPassword) {
|
||||
|
@ -236,7 +241,7 @@ public class MountServerActivity extends AppCompatActivity {
|
|||
|
||||
final DocumentMetadata metadata = DocumentMetadata.createShare(host, share);
|
||||
|
||||
if (mShareManager.containsShare(metadata.getUri().toString())) {
|
||||
if (mShareManager.isShareMounted(metadata.getUri().toString())) {
|
||||
showMessage(R.string.share_already_mounted);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -70,8 +70,8 @@ class MountServerTask extends BiResultTask<Void, Void, Void> {
|
|||
|
||||
@Override
|
||||
public Void run(Void... args) throws IOException {
|
||||
mShareManager.mountServer(
|
||||
mMetadata.getUri().toString(), mDomain, mUsername, mPassword, mChecker);
|
||||
mShareManager.addServer(
|
||||
mMetadata.getUri().toString(), mDomain, mUsername, mPassword, mChecker, true);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import static com.google.android.sambadocumentsprovider.base.DocumentIdHelper.to
|
|||
import static com.google.android.sambadocumentsprovider.base.DocumentIdHelper.toUri;
|
||||
import static com.google.android.sambadocumentsprovider.base.DocumentIdHelper.toUriString;
|
||||
|
||||
import android.app.AuthenticationRequiredException;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
@ -46,6 +47,7 @@ import com.google.android.sambadocumentsprovider.SambaProviderApplication;
|
|||
import com.google.android.sambadocumentsprovider.ShareManager;
|
||||
import com.google.android.sambadocumentsprovider.ShareManager.MountedShareChangeListener;
|
||||
import com.google.android.sambadocumentsprovider.TaskManager;
|
||||
import com.google.android.sambadocumentsprovider.auth.AuthActivity;
|
||||
import com.google.android.sambadocumentsprovider.base.AuthFailedException;
|
||||
import com.google.android.sambadocumentsprovider.base.DirectoryEntry;
|
||||
import com.google.android.sambadocumentsprovider.base.DocumentCursor;
|
||||
|
@ -57,11 +59,11 @@ 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;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -186,7 +188,7 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
|||
if(BuildConfig.DEBUG) Log.d(TAG, "Querying roots.");
|
||||
projection = (projection == null) ? DEFAULT_ROOT_PROJECTION : projection;
|
||||
|
||||
MatrixCursor cursor = new MatrixCursor(projection, mShareManager.size());
|
||||
MatrixCursor cursor = new MatrixCursor(projection);
|
||||
|
||||
cursor.addRow(new Object[] {
|
||||
NetworkBrowser.SMB_BROWSING_URI.toString(),
|
||||
|
@ -197,6 +199,10 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
|||
});
|
||||
|
||||
for (String uri : mShareManager) {
|
||||
if (!mShareManager.isShareMounted(uri)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String name;
|
||||
final Uri parsedUri = Uri.parse(uri);
|
||||
try(CacheResult result = mCache.get(parsedUri)) {
|
||||
|
@ -273,7 +279,7 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
|||
|
||||
@Override
|
||||
public Cursor queryChildDocuments(String documentId, String[] projection, String sortOrder)
|
||||
throws FileNotFoundException {
|
||||
throws FileNotFoundException, AuthenticationRequiredException {
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Querying children documents under " + documentId);
|
||||
projection = (projection == null) ? DEFAULT_DOCUMENT_PROJECTION : projection;
|
||||
|
||||
|
@ -360,7 +366,12 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
|||
return cursor;
|
||||
}
|
||||
} catch (AuthFailedException e) {
|
||||
return buildErrorCursor(projection, R.string.view_folder_denied);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && DocumentMetadata.isShareUri(uri)) {
|
||||
throw new AuthenticationRequiredException(
|
||||
e, AuthActivity.createAuthIntent(getContext(), uri.toString()));
|
||||
} else {
|
||||
return buildErrorCursor(projection, R.string.view_folder_denied);
|
||||
}
|
||||
} catch (FileNotFoundException|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -54,43 +54,53 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/clickable_height"
|
||||
android:layout_below="@id/share_path"
|
||||
android:checked="false"
|
||||
android:text="@string/needs_password"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/password_hide_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/domain"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/clickable_height"
|
||||
android:minLines="1"
|
||||
android:maxLines="1"
|
||||
android:hint="@string/domain"
|
||||
android:inputType="text"
|
||||
android:hint="@string/domain"/>
|
||||
android:maxLines="1"
|
||||
android:minLines="1"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/clickable_height"
|
||||
android:minLines="1"
|
||||
android:maxLines="1"
|
||||
android:hint="@string/username"
|
||||
android:inputType="text"
|
||||
android:hint="@string/username"/>
|
||||
android:maxLines="1"
|
||||
android:minLines="1"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/clickable_height"
|
||||
android:minLines="1"
|
||||
android:maxLines="1"
|
||||
android:inputType="textPassword"
|
||||
android:fontFamily="sans-serif"
|
||||
android:hint="@string/password"/>
|
||||
android:hint="@string/password"
|
||||
android:inputType="textPassword"
|
||||
android:maxLines="1"
|
||||
android:minLines="1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/pin_share"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/pin_this_share"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
|
|
|
@ -49,4 +49,9 @@
|
|||
<string name="no_web_browser">It needs a web browser to send feedback.</string>
|
||||
|
||||
<string name="browsing_root_name">Samba Shares</string>
|
||||
|
||||
<string name="pin_this_share">Pin this share</string>
|
||||
<string name="login">Login</string>
|
||||
<string name="authenticating">Authenticating...</string>
|
||||
<string name="empty_credentials">Username and password can not be empty!</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue