Login Authenticator Added
This commit is contained in:
@@ -2,28 +2,39 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="de.sebse.fuplanner">
|
package="de.sebse.fuplanner">
|
||||||
|
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.AUTHENTICATE_ACCOUNTS"
|
||||||
|
android:maxSdkVersion="22" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.MANAGE_ACCOUNTS"
|
||||||
|
android:maxSdkVersion="22" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
<!-- To auto-complete the email text field in the login form with the user's emails -->
|
||||||
|
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||||
|
<uses-permission android:name="android.permission.READ_PROFILE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
android:fullBackupContent="@xml/backup_descriptor"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/FUTheme"
|
android:theme="@style/FUTheme">
|
||||||
android:fullBackupContent="@xml/backup_descriptor">
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.my.provider"
|
android:authorities="${applicationId}.my.provider"
|
||||||
@@ -31,8 +42,20 @@
|
|||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/provider_paths"/>
|
android:resource="@xml/provider_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".services.newkvv.FUAuthenticatorActivity"
|
||||||
|
android:label="@string/title_activity_fuauthenticator" />
|
||||||
|
<service android:name=".services.newkvv.FUAuthenticatorService">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.accounts.AccountAuthenticator" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data android:name="android.accounts.AccountAuthenticator"
|
||||||
|
android:resource="@xml/authenticator" />
|
||||||
|
</service>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package de.sebse.fuplanner;
|
package de.sebse.fuplanner;
|
||||||
|
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.accounts.AccountManagerCallback;
|
||||||
|
import android.accounts.AccountManagerFuture;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@@ -46,6 +49,7 @@ import de.sebse.fuplanner.services.KVV.KVVListener;
|
|||||||
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.services.News.NewsManager;
|
import de.sebse.fuplanner.services.News.NewsManager;
|
||||||
|
import de.sebse.fuplanner.services.newkvv.AccountGeneral;
|
||||||
import de.sebse.fuplanner.tools.MainActivityListener;
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
import de.sebse.fuplanner.tools.NewAsyncQueue;
|
import de.sebse.fuplanner.tools.NewAsyncQueue;
|
||||||
import de.sebse.fuplanner.tools.Preferences;
|
import de.sebse.fuplanner.tools.Preferences;
|
||||||
@@ -93,10 +97,12 @@ public class MainActivity extends AppCompatActivity
|
|||||||
private boolean mOfflineBanner;
|
private boolean mOfflineBanner;
|
||||||
private final NewAsyncQueue mQueue = new NewAsyncQueue();
|
private final NewAsyncQueue mQueue = new NewAsyncQueue();
|
||||||
private long mDoubleBackToExitPressedOnce = 0;
|
private long mDoubleBackToExitPressedOnce = 0;
|
||||||
|
private AccountManager mAccountManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
mAccountManager = AccountManager.get(this);
|
||||||
int desiredPage = getDefaultFragmentAfterLogin();
|
int desiredPage = getDefaultFragmentAfterLogin();
|
||||||
String desiredData = "";
|
String desiredData = "";
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
@@ -158,6 +164,7 @@ public class MainActivity extends AppCompatActivity
|
|||||||
} else {
|
} else {
|
||||||
mDoubleBackToExitPressedOnce = System.currentTimeMillis();
|
mDoubleBackToExitPressedOnce = System.currentTimeMillis();
|
||||||
showToast(R.string.back_to_exit);
|
showToast(R.string.back_to_exit);
|
||||||
|
getTokenForAccountCreateIfNeeded(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,6 +559,27 @@ public class MainActivity extends AppCompatActivity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void getTokenForAccountCreateIfNeeded(String accountType, String authTokenType) {
|
||||||
|
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthTokenByFeatures(accountType, authTokenType, null, this, null, null,
|
||||||
|
new AccountManagerCallback<Bundle>() {
|
||||||
|
@Override
|
||||||
|
public void run(AccountManagerFuture<Bundle> future) {
|
||||||
|
Bundle bnd = null;
|
||||||
|
try {
|
||||||
|
bnd = future.getResult();
|
||||||
|
final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
|
||||||
|
showToast(((authtoken != null) ? "SUCCESS!\ntoken: " + authtoken : "FAIL"));
|
||||||
|
log.d("udinic", "GetTokenForAccount Bundle is " + bnd);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
showToast(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package de.sebse.fuplanner.services.newkvv;
|
||||||
|
|
||||||
|
public class AccountGeneral {
|
||||||
|
public static final String ACCOUNT_TYPE = "de.sebse.fuplanner.fuauth";
|
||||||
|
public static final String AUTHTOKEN_TYPE_KVV = "KVV";
|
||||||
|
public static final String AUTHTOKEN_TYPE_BLACKBOARD = "Blackboard";
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package de.sebse.fuplanner.services.newkvv;
|
||||||
|
|
||||||
|
import android.accounts.AbstractAccountAuthenticator;
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountAuthenticatorResponse;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.accounts.NetworkErrorException;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
public class FUAuthenticator extends AbstractAccountAuthenticator {
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
public FUAuthenticator(Context context) {
|
||||||
|
super(context);
|
||||||
|
this.mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse, String s) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
|
||||||
|
final Intent intent = new Intent(mContext, FUAuthenticatorActivity.class);
|
||||||
|
intent.putExtra(FUAuthenticatorActivity.ARG_ACCOUNT_TYPE, accountType);
|
||||||
|
intent.putExtra(FUAuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);
|
||||||
|
intent.putExtra(FUAuthenticatorActivity.ARG_IS_ADDING_NEW_ACCOUNT, true);
|
||||||
|
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
|
||||||
|
final Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, Bundle bundle) throws NetworkErrorException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
|
||||||
|
|
||||||
|
// Extract the username and password from the Account Manager, and ask
|
||||||
|
// the server for an appropriate AuthToken.
|
||||||
|
final AccountManager am = AccountManager.get(mContext);
|
||||||
|
|
||||||
|
String authToken = am.peekAuthToken(account, authTokenType);
|
||||||
|
|
||||||
|
// Lets give another try to authenticate the user
|
||||||
|
if (TextUtils.isEmpty(authToken)) {
|
||||||
|
final String password = am.getPassword(account);
|
||||||
|
if (password != null) {
|
||||||
|
try {
|
||||||
|
authToken = new UserLoginTask(account.name, password, authTokenType, null).execute((Void) null).get();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get an authToken - we return it
|
||||||
|
if (!TextUtils.isEmpty(authToken)) {
|
||||||
|
final Bundle result = new Bundle();
|
||||||
|
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
|
||||||
|
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
|
||||||
|
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, then we couldn't access the user's password - so we
|
||||||
|
// need to re-prompt them for their credentials. We do that by creating
|
||||||
|
// an intent to display our AuthenticatorActivity.
|
||||||
|
final Intent intent = new Intent(mContext, FUAuthenticatorActivity.class);
|
||||||
|
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
|
||||||
|
intent.putExtra(FUAuthenticatorActivity.ARG_ACCOUNT_TYPE, account.type);
|
||||||
|
intent.putExtra(FUAuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);
|
||||||
|
final Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthTokenLabel(String s) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String[] strings) throws NetworkErrorException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,329 @@
|
|||||||
|
package de.sebse.fuplanner.services.newkvv;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountAuthenticatorActivity;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import android.app.LoaderManager.LoaderCallbacks;
|
||||||
|
|
||||||
|
import android.content.CursorLoader;
|
||||||
|
import android.content.Loader;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.ContactsContract;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.AutoCompleteTextView;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
|
||||||
|
import static de.sebse.fuplanner.services.newkvv.UserLoginTask.PARAM_USER_PASS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A login screen that offers login via email/password.
|
||||||
|
*/
|
||||||
|
public class FUAuthenticatorActivity extends AccountAuthenticatorActivity implements LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Id to identity READ_CONTACTS permission request.
|
||||||
|
*/
|
||||||
|
private static final int REQUEST_READ_CONTACTS = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep track of the login task to ensure we can cancel it if requested.
|
||||||
|
*/
|
||||||
|
UserLoginTask mAuthTask = null;
|
||||||
|
|
||||||
|
// UI references.
|
||||||
|
private AutoCompleteTextView mEmailView;
|
||||||
|
EditText mPasswordView;
|
||||||
|
private View mProgressView;
|
||||||
|
private View mLoginFormView;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static final String ARG_ACCOUNT_TYPE = "ARG_ACCOUNT_TYPE";
|
||||||
|
public static final String ARG_AUTH_TYPE = "ARG_AUTH_TYPE";
|
||||||
|
public static final String ARG_IS_ADDING_NEW_ACCOUNT = "ARG_IS_ADDING_NEW_ACCOUNT";
|
||||||
|
private String mAccountType;
|
||||||
|
private String mAuthTokenType;
|
||||||
|
private boolean mIsAddingNewAccount;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
mAccountType = getIntent().getStringExtra(ARG_ACCOUNT_TYPE);
|
||||||
|
mAuthTokenType = getIntent().getStringExtra(ARG_AUTH_TYPE);
|
||||||
|
mIsAddingNewAccount = getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, false);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_fu_authenticator);
|
||||||
|
// Set up the login form.
|
||||||
|
mEmailView = (AutoCompleteTextView) findViewById(R.id.email);
|
||||||
|
populateAutoComplete();
|
||||||
|
|
||||||
|
mPasswordView = (EditText) findViewById(R.id.password);
|
||||||
|
mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
|
||||||
|
if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) {
|
||||||
|
attemptLogin();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
|
||||||
|
mEmailSignInButton.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
attemptLogin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mLoginFormView = findViewById(R.id.login_form);
|
||||||
|
mProgressView = findViewById(R.id.login_progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateAutoComplete() {
|
||||||
|
if (!mayRequestContacts()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLoaderManager().initLoader(0, null, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean mayRequestContacts() {
|
||||||
|
/*if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (shouldShowRequestPermissionRationale(READ_CONTACTS)) {
|
||||||
|
Snackbar.make(mEmailView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
|
||||||
|
.setAction(android.R.string.ok, new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
|
public void onClick(View v) {
|
||||||
|
requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
|
||||||
|
}*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback received when a permissions request has been completed.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||||
|
@NonNull int[] grantResults) {
|
||||||
|
if (requestCode == REQUEST_READ_CONTACTS) {
|
||||||
|
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
populateAutoComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to sign in or register the account specified by the login form.
|
||||||
|
* If there are form errors (invalid email, missing fields, etc.), the
|
||||||
|
* errors are presented and no actual login attempt is made.
|
||||||
|
*/
|
||||||
|
private void attemptLogin() {
|
||||||
|
if (mAuthTask != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset errors.
|
||||||
|
mEmailView.setError(null);
|
||||||
|
mPasswordView.setError(null);
|
||||||
|
|
||||||
|
// Store values at the time of the login attempt.
|
||||||
|
String email = mEmailView.getText().toString();
|
||||||
|
String password = mPasswordView.getText().toString();
|
||||||
|
|
||||||
|
boolean cancel = false;
|
||||||
|
View focusView = null;
|
||||||
|
|
||||||
|
// Check for a valid password, if the user entered one.
|
||||||
|
if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
|
||||||
|
mPasswordView.setError(getString(R.string.error_invalid_password));
|
||||||
|
focusView = mPasswordView;
|
||||||
|
cancel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a valid email address.
|
||||||
|
if (TextUtils.isEmpty(email)) {
|
||||||
|
mEmailView.setError(getString(R.string.error_field_required));
|
||||||
|
focusView = mEmailView;
|
||||||
|
cancel = true;
|
||||||
|
} else if (!isEmailValid(email)) {
|
||||||
|
mEmailView.setError(getString(R.string.error_invalid_email));
|
||||||
|
focusView = mEmailView;
|
||||||
|
cancel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancel) {
|
||||||
|
// There was an error; don't attempt login and focus the first
|
||||||
|
// form field with an error.
|
||||||
|
focusView.requestFocus();
|
||||||
|
} else {
|
||||||
|
// Show a progress spinner, and kick off a background task to
|
||||||
|
// perform the user login attempt.
|
||||||
|
showProgress(true);
|
||||||
|
mAuthTask = new UserLoginTask(email, password, mAuthTokenType, this);
|
||||||
|
mAuthTask.execute((Void) null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEmailValid(String email) {
|
||||||
|
//TODO: Replace this with your own logic
|
||||||
|
return email.contains("@");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPasswordValid(String password) {
|
||||||
|
//TODO: Replace this with your own logic
|
||||||
|
return password.length() > 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the progress UI and hides the login form.
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
|
||||||
|
void showProgress(final boolean show) {
|
||||||
|
// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
|
||||||
|
// for very easy animations. If available, use these APIs to fade-in
|
||||||
|
// the progress spinner.
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
|
||||||
|
int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
|
||||||
|
|
||||||
|
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
|
||||||
|
mLoginFormView.animate().setDuration(shortAnimTime).alpha(
|
||||||
|
show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||||
|
mProgressView.animate().setDuration(shortAnimTime).alpha(
|
||||||
|
show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// The ViewPropertyAnimator APIs are not available, so simply show
|
||||||
|
// and hide the relevant UI components.
|
||||||
|
mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||||
|
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||||
|
return new CursorLoader(this,
|
||||||
|
// Retrieve data rows for the device user's 'profile' contact.
|
||||||
|
Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
|
||||||
|
ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION,
|
||||||
|
|
||||||
|
// Select only email addresses.
|
||||||
|
ContactsContract.Contacts.Data.MIMETYPE +
|
||||||
|
" = ?", new String[]{ContactsContract.CommonDataKinds.Email
|
||||||
|
.CONTENT_ITEM_TYPE},
|
||||||
|
|
||||||
|
// Show primary email addresses first. Note that there won't be
|
||||||
|
// a primary email address if the user hasn't specified one.
|
||||||
|
ContactsContract.Contacts.Data.IS_PRIMARY + " DESC");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||||
|
List<String> emails = new ArrayList<>();
|
||||||
|
cursor.moveToFirst();
|
||||||
|
while (!cursor.isAfterLast()) {
|
||||||
|
emails.add(cursor.getString(ProfileQuery.ADDRESS));
|
||||||
|
cursor.moveToNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
addEmailsToAutoComplete(emails);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEmailsToAutoComplete(List<String> emailAddressCollection) {
|
||||||
|
//Create adapter to tell the AutoCompleteTextView what to show in its dropdown list.
|
||||||
|
ArrayAdapter<String> adapter =
|
||||||
|
new ArrayAdapter<>(FUAuthenticatorActivity.this,
|
||||||
|
android.R.layout.simple_dropdown_item_1line, emailAddressCollection);
|
||||||
|
|
||||||
|
mEmailView.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private interface ProfileQuery {
|
||||||
|
String[] PROJECTION = {
|
||||||
|
ContactsContract.CommonDataKinds.Email.ADDRESS,
|
||||||
|
ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
|
||||||
|
};
|
||||||
|
|
||||||
|
int ADDRESS = 0;
|
||||||
|
int IS_PRIMARY = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void finishLogin(Intent intent) {
|
||||||
|
String accountName = intent.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
|
||||||
|
String accountPassword = intent.getStringExtra(PARAM_USER_PASS);
|
||||||
|
final Account account = new Account(accountName, intent.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE));
|
||||||
|
final AccountManager mAccountManager = AccountManager.get(this);
|
||||||
|
|
||||||
|
if (getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, false)) {
|
||||||
|
String authtoken = intent.getStringExtra(AccountManager.KEY_AUTHTOKEN);
|
||||||
|
String authtokenType = mAuthTokenType;
|
||||||
|
|
||||||
|
// Creating the account on the device and setting the auth token we got
|
||||||
|
// (Not setting the auth token will cause another call to the server to authenticate the user)
|
||||||
|
mAccountManager.addAccountExplicitly(account, accountPassword, null);
|
||||||
|
mAccountManager.setAuthToken(account, authtokenType, authtoken);
|
||||||
|
} else {
|
||||||
|
mAccountManager.setPassword(account, accountPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
setAccountAuthenticatorResult(intent.getExtras());
|
||||||
|
setResult(RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package de.sebse.fuplanner.services.newkvv;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
public class FUAuthenticatorService extends Service {
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
FUAuthenticator authenticator = new FUAuthenticator(this);
|
||||||
|
return authenticator.getIBinder();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package de.sebse.fuplanner.services.newkvv;
|
||||||
|
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an asynchronous login/registration task used to authenticate
|
||||||
|
* the user.
|
||||||
|
*/
|
||||||
|
public class UserLoginTask extends AsyncTask<Void, Void, String> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dummy authentication store containing known user names and passwords.
|
||||||
|
* TODO: remove after connecting to a real authentication system.
|
||||||
|
*/
|
||||||
|
private static final String[] DUMMY_CREDENTIALS = new String[]{
|
||||||
|
"foo@example.com:hello", "bar@example.com:world"
|
||||||
|
};
|
||||||
|
static final String PARAM_USER_PASS = "PARAM_USER_PASS";
|
||||||
|
|
||||||
|
private final String mEmail;
|
||||||
|
private final String mPassword;
|
||||||
|
private String mTokenType;
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
@Nullable
|
||||||
|
private FUAuthenticatorActivity mActivity;
|
||||||
|
|
||||||
|
UserLoginTask(String email, String password, String tokenType, @Nullable FUAuthenticatorActivity activity) {
|
||||||
|
mEmail = email;
|
||||||
|
mPassword = password;
|
||||||
|
mTokenType = tokenType;
|
||||||
|
mActivity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doInBackground(Void... params) {
|
||||||
|
// TODO: attempt authentication against a network service.
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Simulate network access.
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String credential : DUMMY_CREDENTIALS) {
|
||||||
|
String[] pieces = credential.split(":");
|
||||||
|
if (pieces[0].equals(mEmail)) {
|
||||||
|
// Account exists, return true if the password matches.
|
||||||
|
return pieces[1].equals(mPassword) ? "auth token here" : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: register the new account here.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(final String success) {
|
||||||
|
if (mActivity == null || mActivity.isFinishing())
|
||||||
|
return;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mActivity.isDestroyed())
|
||||||
|
return;
|
||||||
|
mActivity.mAuthTask = null;
|
||||||
|
mActivity.showProgress(false);
|
||||||
|
|
||||||
|
if (success != null) {
|
||||||
|
final Intent res = new Intent();
|
||||||
|
res.putExtra(AccountManager.KEY_ACCOUNT_NAME, mEmail);
|
||||||
|
res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountGeneral.ACCOUNT_TYPE);
|
||||||
|
res.putExtra(AccountManager.KEY_AUTHTOKEN, success);
|
||||||
|
res.putExtra(PARAM_USER_PASS, mPassword);
|
||||||
|
mActivity.finishLogin(res);
|
||||||
|
} else {
|
||||||
|
mActivity.mPasswordView.setError(mActivity.getString(R.string.error_incorrect_password));
|
||||||
|
mActivity.mPasswordView.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled() {
|
||||||
|
mActivity.mAuthTask = null;
|
||||||
|
mActivity.showProgress(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
78
app/src/main/res/layout/activity_fu_authenticator.xml
Normal file
78
app/src/main/res/layout/activity_fu_authenticator.xml
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
tools:context=".services.newkvv.FUAuthenticatorActivity">
|
||||||
|
|
||||||
|
<!-- Login progress -->
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/login_progress"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/login_form"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/email_login_form"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<AutoCompleteTextView
|
||||||
|
android:id="@+id/email"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/prompt_email"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/password"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/prompt_password"
|
||||||
|
android:imeActionId="6"
|
||||||
|
android:imeActionLabel="@string/action_sign_in_short"
|
||||||
|
android:imeOptions="actionUnspecified"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/email_sign_in_button"
|
||||||
|
style="?android:textAppearanceSmall"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/action_sign_in"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</LinearLayout>
|
||||||
@@ -90,4 +90,17 @@
|
|||||||
<string name="update_news">Update News</string>
|
<string name="update_news">Update News</string>
|
||||||
<string name="tricks">Tips/Tricks</string>
|
<string name="tricks">Tips/Tricks</string>
|
||||||
<string name="back_to_exit">Please click BACK again to exit!</string>
|
<string name="back_to_exit">Please click BACK again to exit!</string>
|
||||||
|
<string name="title_activity_fuauthenticator">Sign in</string>
|
||||||
|
<!-- Strings related to login -->
|
||||||
|
<string name="prompt_email">Email</string>
|
||||||
|
<string name="prompt_password">Password (optional)</string>
|
||||||
|
<string name="action_sign_in">Sign in or register</string>
|
||||||
|
<string name="action_sign_in_short">Sign in</string>
|
||||||
|
<string name="error_invalid_email">This email address is invalid</string>
|
||||||
|
<string name="error_invalid_password">This password is too short</string>
|
||||||
|
<string name="error_incorrect_password">This password is incorrect</string>
|
||||||
|
<string name="error_field_required">This field is required</string>
|
||||||
|
<string name="permission_rationale">"Contacts permissions are needed for providing email
|
||||||
|
completions."
|
||||||
|
</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
6
app/src/main/res/xml/authenticator.xml
Normal file
6
app/src/main/res/xml/authenticator.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:accountType="de.sebse.fuplanner.fuauth"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:smallIcon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"/>
|
||||||
Reference in New Issue
Block a user