Merge "commons" into the project root directory

This commit is contained in:
Yusuke Matsubara 2016-07-02 16:20:43 +09:00
parent d42db0612e
commit b4231bbfdc
324 changed files with 22 additions and 23 deletions

View file

@ -0,0 +1,152 @@
package fr.free.nrw.commons.auth;
import java.io.IOException;
import android.accounts.OperationCanceledException;
import android.accounts.*;
import android.os.*;
import android.support.v7.app.AppCompatActivity;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Utils;
public class AuthenticatedActivity extends AppCompatActivity {
String accountType;
CommonsApplication app;
private String authCookie;
public AuthenticatedActivity(String accountType) {
this.accountType = accountType;
}
private class GetAuthCookieTask extends AsyncTask<Void, String, String> {
private Account account;
private AccountManager accountManager;
public GetAuthCookieTask(Account account, AccountManager accountManager) {
this.account = account;
this.accountManager = accountManager;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(result != null) {
authCookie = result;
onAuthCookieAcquired(result);
} else {
onAuthFailure();
}
}
@Override
protected String doInBackground(Void... params) {
try {
return accountManager.blockingGetAuthToken(account, "", false);
} catch (OperationCanceledException e) {
e.printStackTrace();
return null;
} catch (AuthenticatorException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
private class AddAccountTask extends AsyncTask<Void, String, String> {
private AccountManager accountManager;
public AddAccountTask(AccountManager accountManager) {
this.accountManager = accountManager;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(result != null) {
Account[] allAccounts =accountManager.getAccountsByType(accountType);
Account curAccount = allAccounts[0];
GetAuthCookieTask getCookieTask = new GetAuthCookieTask(curAccount, accountManager);
getCookieTask.execute();
} else {
onAuthFailure();
}
}
@Override
protected String doInBackground(Void... params) {
AccountManagerFuture<Bundle> resultFuture = accountManager.addAccount(accountType, null, null, null, AuthenticatedActivity.this, null, null);
Bundle result;
try {
result = resultFuture.getResult();
} catch (OperationCanceledException e) {
e.printStackTrace();
return null;
} catch (AuthenticatorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
if(result.containsKey(AccountManager.KEY_ACCOUNT_NAME)) {
return result.getString(AccountManager.KEY_ACCOUNT_NAME);
} else {
return null;
}
}
}
protected void requestAuthToken() {
if(authCookie != null) {
onAuthCookieAcquired(authCookie);
return;
}
AccountManager accountManager = AccountManager.get(this);
Account curAccount = app.getCurrentAccount();
if(curAccount == null) {
AddAccountTask addAccountTask = new AddAccountTask(accountManager);
// This AsyncTask blocks until the Login Activity returns
// And since in Android 4.x+ only one background thread runs all AsyncTasks
// And since LoginActivity can't return until it's own AsyncTask (that does the login)
// returns, we have a deadlock!
// Fixed by explicitly asking this to be executed in parallel
// See: https://groups.google.com/forum/?fromgroups=#!topic/android-developers/8M0RTFfO7-M
Utils.executeAsyncTask(addAccountTask);
} else {
GetAuthCookieTask task = new GetAuthCookieTask(curAccount, accountManager);
task.execute();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (CommonsApplication)this.getApplicationContext();
if(savedInstanceState != null) {
authCookie = savedInstanceState.getString("authCookie");
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("authCookie", authCookie);
}
protected void onAuthCookieAcquired(String authCookie) {
}
protected void onAuthFailure() {
}
}

View file

@ -0,0 +1,230 @@
package fr.free.nrw.commons.auth;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.NavUtils;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.EventLog;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
public class LoginActivity extends AccountAuthenticatorActivity {
public static final String PARAM_USERNAME = "fr.free.nrw.commons.login.username";
private CommonsApplication app;
Button loginButton;
Button signupButton;
EditText usernameEdit;
EditText passwordEdit;
private class LoginTask extends AsyncTask<String, String, String> {
Activity context;
ProgressDialog dialog;
String username;
String password;
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Log.d("Commons", "Login done!");
EventLog.schema(CommonsApplication.EVENT_LOGIN_ATTEMPT)
.param("username", username)
.param("result", result)
.log();
if (result.equals("Success")) {
dialog.dismiss();
Toast successToast = Toast.makeText(context, R.string.login_success, Toast.LENGTH_SHORT);
successToast.show();
Account account = new Account(username, WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
boolean accountCreated = AccountManager.get(context).addAccountExplicitly(account, password, null);
Bundle extras = context.getIntent().getExtras();
if (extras != null) {
if (accountCreated) { // Pass the new account back to the account manager
AccountAuthenticatorResponse response = extras.getParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
Bundle authResult = new Bundle();
authResult.putString(AccountManager.KEY_ACCOUNT_NAME, username);
authResult.putString(AccountManager.KEY_ACCOUNT_TYPE, WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
response.onResult(authResult);
}
}
// FIXME: If the user turns it off, it shouldn't be auto turned back on
ContentResolver.setSyncAutomatically(account, ContributionsContentProvider.AUTHORITY, true); // Enable sync by default!
ContentResolver.setSyncAutomatically(account, ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
context.finish();
} else {
int response;
if(result.equals("NetworkFailure")) {
response = R.string.login_failed_network;
} else if(result.equals("NotExists") || result.equals("Illegal") || result.equals("NotExists")) {
response = R.string.login_failed_username;
passwordEdit.setText("");
} else if(result.equals("EmptyPass") || result.equals("WrongPass") || result.equals("WrongPluginPass")) {
response = R.string.login_failed_password;
passwordEdit.setText("");
} else if(result.equals("Throttled")) {
response = R.string.login_failed_throttled;
} else if(result.equals("Blocked")) {
response = R.string.login_failed_blocked;
} else {
// Should never really happen
Log.d("Commons", "Login failed with reason: " + result);
response = R.string.login_failed_generic;
}
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_LONG).show();
dialog.cancel();
}
}
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(context);
dialog.setIndeterminate(true);
dialog.setTitle(getString(R.string.logging_in_title));
dialog.setMessage(getString(R.string.logging_in_message));
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
LoginTask(Activity context) {
this.context = context;
}
@Override
protected String doInBackground(String... params) {
username = params[0];
password = params[1];
try {
return app.getApi().login(username, password);
} catch (IOException e) {
// Do something better!
return "NetworkFailure";
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (CommonsApplication) this.getApplicationContext();
setContentView(R.layout.activity_login);
loginButton = (Button) findViewById(R.id.loginButton);
signupButton = (Button) findViewById(R.id.signupButton);
usernameEdit = (EditText) findViewById(R.id.loginUsername);
passwordEdit = (EditText) findViewById(R.id.loginPassword);
final LoginActivity that = this;
TextWatcher loginEnabler = new TextWatcher() {
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) { }
public void onTextChanged(CharSequence charSequence, int start, int count, int after) { }
public void afterTextChanged(Editable editable) {
if(usernameEdit.getText().length() != 0 && passwordEdit.getText().length() != 0) {
loginButton.setEnabled(true);
} else {
loginButton.setEnabled(false);
}
}
};
usernameEdit.addTextChangedListener(loginEnabler);
passwordEdit.addTextChangedListener(loginEnabler);
passwordEdit.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
if (loginButton.isEnabled()) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
performLogin();
return true;
} else if ((keyEvent != null) && keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
performLogin();
return true;
}
}
return false;
}
});
signupButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://commons.wikimedia.org/wiki/Special:UserLogin/signup")));
}
});
loginButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
that.performLogin();
}
});
if (savedInstanceState == null) {
Intent welcomeIntent = new Intent(this, WelcomeActivity.class);
startActivity(welcomeIntent);
}
}
private void performLogin() {
String username = usernameEdit.getText().toString();
// Because Mediawiki is upercase-first-char-then-case-sensitive :)
String canonicalUsername = username.substring(0,1).toUpperCase() + username.substring(1);
String password = passwordEdit.getText().toString();
Log.d("Commons", "Login to start!");
LoginTask task = new LoginTask(this);
task.execute(canonicalUsername, password);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_login, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -0,0 +1,106 @@
package fr.free.nrw.commons.auth;
import java.io.IOException;
import android.accounts.*;
import android.content.*;
import android.os.*;
import org.mediawiki.api.*;
import fr.free.nrw.commons.CommonsApplication;
public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
public static final String COMMONS_ACCOUNT_TYPE = "fr.free.nrw.commons";
private Context context;
public WikiAccountAuthenticator(Context context) {
super(context);
this.context = context;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
// TODO Auto-generated method stub
final Intent intent = new Intent(context, LoginActivity.class);
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 response, Account account, Bundle options) throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
// TODO Auto-generated method stub
return null;
}
private String getAuthCookie(String username, String password) throws IOException {
MWApi api = CommonsApplication.createMWApi();
String result = api.login(username, password);
if(result.equals("Success")) {
return api.getAuthCookie();
} else {
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(context);
final String password = am.getPassword(account);
if (password != null) {
String authCookie;
try {
authCookie = getAuthCookie(account.name, password);
} catch (IOException e) {
// Network error!
e.printStackTrace();
throw new NetworkErrorException(e);
}
if (authCookie != null) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, COMMONS_ACCOUNT_TYPE);
result.putString(AccountManager.KEY_AUTHTOKEN, authCookie);
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 panel.
final Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra(LoginActivity.PARAM_USERNAME, account.name);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
// TODO Auto-generated method stub
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
final Bundle result = new Bundle();
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
return result;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}
}

View file

@ -0,0 +1,23 @@
package fr.free.nrw.commons.auth;
import android.app.*;
import android.content.*;
import android.os.*;
public class WikiAccountAuthenticatorService extends Service{
private static WikiAccountAuthenticator wikiAccountAuthenticator = null;
@Override
public IBinder onBind(Intent intent) {
if (!intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
return null;
}
if(wikiAccountAuthenticator == null) {
wikiAccountAuthenticator = new WikiAccountAuthenticator(this);
}
return wikiAccountAuthenticator.getIBinder();
}
}