diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2e4bada1b..e73336ac8 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -8,6 +8,9 @@ android:targetSdkVersion="15" /> + + + @@ -24,11 +27,27 @@ android:label="@string/title_activity_share" > + + - + + + + + + + + + + \ No newline at end of file diff --git a/libs/java-mwapi.jar b/libs/java-mwapi.jar index 33e68af74..77ce4d244 100644 Binary files a/libs/java-mwapi.jar and b/libs/java-mwapi.jar differ diff --git a/src/org/wikimedia/commons/CommonsApplication.java b/src/org/wikimedia/commons/CommonsApplication.java index fbf1470ff..b38028426 100644 --- a/src/org/wikimedia/commons/CommonsApplication.java +++ b/src/org/wikimedia/commons/CommonsApplication.java @@ -10,11 +10,9 @@ import org.apache.http.params.CoreProtocolPNames; public class CommonsApplication extends Application { private MWApi api; - - @Override - public void onCreate() { - // TODO Auto-generated method stub - super.onCreate(); + public static final String API_URL = "http://test.wikipedia.org/w/api.php"; + + public static MWApi createMWApi() { DefaultHttpClient client = new DefaultHttpClient(); // Because WMF servers support only HTTP/1.0. Biggest difference that // this makes is support for Chunked Transfer Encoding. @@ -22,7 +20,14 @@ public class CommonsApplication extends Application { // throws up. client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_0); - api = new MWApi("http://test.wikipedia.org/w/api.php", client); + return new MWApi(API_URL, client); + } + + @Override + public void onCreate() { + // TODO Auto-generated method stub + super.onCreate(); + api = createMWApi(); } public MWApi getApi() { diff --git a/src/org/wikimedia/commons/ShareActivity.java b/src/org/wikimedia/commons/ShareActivity.java index 174bd49a1..7fe4ae2b3 100644 --- a/src/org/wikimedia/commons/ShareActivity.java +++ b/src/org/wikimedia/commons/ShareActivity.java @@ -4,6 +4,7 @@ import java.io.*; import org.mediawiki.api.ApiResult; import org.mediawiki.api.MWApi; +import org.wikimedia.commons.auth.LoginActivity; import android.net.Uri; import android.os.AsyncTask; diff --git a/src/org/wikimedia/commons/UploadService.java b/src/org/wikimedia/commons/UploadService.java index 8cfc2b919..abe02855c 100644 --- a/src/org/wikimedia/commons/UploadService.java +++ b/src/org/wikimedia/commons/UploadService.java @@ -11,6 +11,7 @@ import android.content.*; import android.os.*; import android.support.v4.app.NotificationCompat; import android.util.Log; +import android.view.View; import android.widget.RemoteViews; import android.widget.Toast; import android.net.*; @@ -83,10 +84,6 @@ public class UploadService extends IntentService { length = this.getContentResolver().openAssetFileDescriptor(mediaUri, "r").getLength(); } catch (FileNotFoundException e) { throw new RuntimeException(e); - } catch (IOException e) { - //I'm hoping there are no streams that can be opened and read only once. - e.printStackTrace(); - throw new RuntimeException(e); } notificationView = new RemoteViews(getPackageName(), R.layout.layout_upload_progress); diff --git a/src/org/wikimedia/commons/LoginActivity.java b/src/org/wikimedia/commons/auth/LoginActivity.java similarity index 55% rename from src/org/wikimedia/commons/LoginActivity.java rename to src/org/wikimedia/commons/auth/LoginActivity.java index 6a20851e9..6c8f255c5 100644 --- a/src/org/wikimedia/commons/LoginActivity.java +++ b/src/org/wikimedia/commons/auth/LoginActivity.java @@ -1,9 +1,20 @@ -package org.wikimedia.commons; +package org.wikimedia.commons.auth; import java.io.IOException; +import org.wikimedia.commons.CommonsApplication; +import org.wikimedia.commons.R; +import org.wikimedia.commons.R.id; +import org.wikimedia.commons.R.layout; +import org.wikimedia.commons.R.menu; +import org.wikimedia.commons.R.string; + import android.os.AsyncTask; import android.os.Bundle; +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.view.*; @@ -12,51 +23,69 @@ import android.widget.EditText; import android.widget.Toast; import android.support.v4.app.NavUtils; -public class LoginActivity extends Activity { - +public class LoginActivity extends AccountAuthenticatorActivity { + + public static final String PARAM_USERNAME = "org.wikimedia.commons.login.username"; + private CommonsApplication app; - + Button loginButton; EditText usernameEdit; EditText passwordEdit; - + private class LoginTask extends AsyncTask { Activity context; ProgressDialog dialog; + String username; + String password; + @Override protected void onPostExecute(String result) { super.onPostExecute(result); - if(result.equals("Success")) { + if (result.equals("Success")) { dialog.cancel(); 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); + } + } context.finish(); } else { Toast failureToast = Toast.makeText(context, R.string.login_failed, Toast.LENGTH_LONG); failureToast.show(); } - + } @Override protected void onPreExecute() { super.onPreExecute(); - dialog = new ProgressDialog(context); + dialog = new ProgressDialog(context); dialog.setIndeterminate(true); dialog.setTitle(getString(R.string.logging_in_title)); dialog.setMessage(getString(R.string.logging_in_message)); dialog.show(); } - + LoginTask(Activity context) { this.context = context; } - + @Override protected String doInBackground(String... params) { - String username = params[0]; - String password = params[1]; + username = params[0]; + password = params[1]; try { return app.getApi().login(username, password); } catch (IOException e) { @@ -64,24 +93,24 @@ public class LoginActivity extends Activity { return "Failure"; } } - + } - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - app = (CommonsApplication)this.getApplicationContext(); + app = (CommonsApplication) this.getApplicationContext(); setContentView(R.layout.activity_login); - loginButton = (Button)findViewById(R.id.loginButton); - usernameEdit = (EditText)findViewById(R.id.loginUsername); - passwordEdit = (EditText)findViewById(R.id.loginPassword); + loginButton = (Button) findViewById(R.id.loginButton); + usernameEdit = (EditText) findViewById(R.id.loginUsername); + passwordEdit = (EditText) findViewById(R.id.loginPassword); final Activity that = this; loginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String username = usernameEdit.getText().toString(); String password = passwordEdit.getText().toString(); - + LoginTask task = new LoginTask(that); task.execute(username, password); } @@ -94,18 +123,14 @@ public class LoginActivity extends Activity { return true; } - @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case android.R.id.home: - NavUtils.navigateUpFromSameTask(this); - return true; + case android.R.id.home: + NavUtils.navigateUpFromSameTask(this); + return true; } return super.onOptionsItemSelected(item); } - - - } diff --git a/src/org/wikimedia/commons/auth/WikiAccountAuthenticator.java b/src/org/wikimedia/commons/auth/WikiAccountAuthenticator.java new file mode 100644 index 000000000..1da6d5259 --- /dev/null +++ b/src/org/wikimedia/commons/auth/WikiAccountAuthenticator.java @@ -0,0 +1,119 @@ +package org.wikimedia.commons.auth; + +import java.io.IOException; + +import org.mediawiki.api.ApiResult; +import org.mediawiki.api.MWApi; +import org.wikimedia.commons.CommonsApplication; + +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; + +public class WikiAccountAuthenticator extends AbstractAccountAuthenticator { + + public static final String COMMONS_ACCOUNT_TYPE = "org.wikimedia.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)context.getApplicationContext()).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 { + // If the caller requested an authToken type we don't support, then + // return an error + if (!authTokenType.equals(COMMONS_ACCOUNT_TYPE)) { + final Bundle result = new Bundle(); + result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType"); + return result; + } + + // 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! + 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; + } + +} diff --git a/src/org/wikimedia/commons/auth/WikiAccountAuthenticatorService.java b/src/org/wikimedia/commons/auth/WikiAccountAuthenticatorService.java new file mode 100644 index 000000000..c22c970aa --- /dev/null +++ b/src/org/wikimedia/commons/auth/WikiAccountAuthenticatorService.java @@ -0,0 +1,23 @@ +package org.wikimedia.commons.auth; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +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(); + } + +}