Make password-setting AsyncTask survive configuration changes (screen rotation)
Robust code derived from: http://stackoverflow.com/a/12303649
This commit is contained in:
@@ -1,62 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<ScrollView 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:padding="10dp"
|
||||
android:orientation="vertical" >
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/old_password"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password_old"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textPassword" >
|
||||
android:orientation="vertical" >
|
||||
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
<fragment
|
||||
android:id="@+id/set_password_frag"
|
||||
android:name="i2p.bote.SetPasswordFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:layout="@layout/fragment_set_password" >
|
||||
</fragment>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/new_password"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
<FrameLayout
|
||||
android:id="@+id/password_waiter_frag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password_new"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/confirm_new_password"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/submit_password"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="OK" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/error_color" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
19
res/layout/dialog_status.xml
Normal file
19
res/layout/dialog_status.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminateOnly="true" />
|
||||
|
||||
</LinearLayout>
|
||||
70
res/layout/fragment_set_password.xml
Normal file
70
res/layout/fragment_set_password.xml
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingTop="10dp" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/old_password"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password_old"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textPassword" >
|
||||
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/new_password"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password_new"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/confirm_new_password"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/submit_password"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="OK" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/error_color" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -51,4 +51,5 @@
|
||||
<string name="old_password">Old password:</string>
|
||||
<string name="new_password">New password:</string>
|
||||
<string name="confirm_new_password">Confirm new password:</string>
|
||||
<string name="password_changed">Password changed successfully</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,88 +1,15 @@
|
||||
package i2p.bote;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class SetPasswordActivity extends ActionBarActivity {
|
||||
TextView error;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_set_password);
|
||||
|
||||
// Enable ActionBar app icon to behave as action to go back
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
final EditText oldField = (EditText) findViewById(R.id.password_old);
|
||||
final EditText newField = (EditText) findViewById(R.id.password_new);
|
||||
final EditText confirmField = (EditText) findViewById(R.id.password_confirm);
|
||||
final Button b = (Button) findViewById(R.id.submit_password);
|
||||
error = (TextView) findViewById(R.id.error);
|
||||
|
||||
b.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
String oldPassword = oldField.getText().toString();
|
||||
String newPassword = newField.getText().toString();
|
||||
String confirmNewPassword = confirmField.getText().toString();
|
||||
|
||||
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(newField.getWindowToken(), 0);
|
||||
|
||||
new PasswordWaiter().execute(oldPassword, newPassword, confirmNewPassword);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class PasswordWaiter extends AsyncTask<String, String, String> {
|
||||
private final ProgressDialog dialog = new ProgressDialog(SetPasswordActivity.this);
|
||||
|
||||
protected void onPreExecute() {
|
||||
dialog.setCancelable(false);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
protected String doInBackground(String... params) {
|
||||
StatusListener lsnr = new StatusListener() {
|
||||
public void updateStatus(String status) {
|
||||
publishProgress(status);
|
||||
}
|
||||
};
|
||||
try {
|
||||
I2PBote.getInstance().changePassword(
|
||||
params[0].getBytes(),
|
||||
params[1].getBytes(),
|
||||
params[2].getBytes(),
|
||||
lsnr);
|
||||
return null;
|
||||
} catch (Throwable e) {
|
||||
cancel(false);
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onProgressUpdate(String... values) {
|
||||
dialog.setMessage(values[0]);
|
||||
}
|
||||
|
||||
protected void onCancelled(String result) {
|
||||
error.setText(result);
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
protected void onPostExecute(String result) {
|
||||
dialog.dismiss();
|
||||
// Password changed successfully
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
171
src/i2p/bote/SetPasswordFragment.java
Normal file
171
src/i2p/bote/SetPasswordFragment.java
Normal file
@@ -0,0 +1,171 @@
|
||||
package i2p.bote;
|
||||
|
||||
import i2p.bote.util.RobustAsyncTask;
|
||||
import i2p.bote.util.TaskFragment;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class SetPasswordFragment extends Fragment {
|
||||
// Code to identify the fragment that is calling onActivityResult().
|
||||
static final int PASSWORD_WAITER = 0;
|
||||
// Tag so we can find the task fragment again, in another
|
||||
// instance of this fragment after rotation.
|
||||
static final String PASSWORD_WAITER_TAG = "passwordWaiterTask";
|
||||
|
||||
private FragmentManager mFM;
|
||||
Button mSubmit;
|
||||
TextView mError;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mFM = getFragmentManager();
|
||||
PasswordWaiterFrag f = (PasswordWaiterFrag) mFM.findFragmentByTag(PASSWORD_WAITER_TAG);
|
||||
if (f != null)
|
||||
f.setTargetFragment(this, PASSWORD_WAITER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_set_password, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
final EditText oldField = (EditText) view.findViewById(R.id.password_old);
|
||||
final EditText newField = (EditText) view.findViewById(R.id.password_new);
|
||||
final EditText confirmField = (EditText) view.findViewById(R.id.password_confirm);
|
||||
mSubmit = (Button) view.findViewById(R.id.submit_password);
|
||||
mError = (TextView) view.findViewById(R.id.error);
|
||||
|
||||
mSubmit.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
String oldPassword = oldField.getText().toString();
|
||||
String newPassword = newField.getText().toString();
|
||||
String confirmNewPassword = confirmField.getText().toString();
|
||||
|
||||
InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(newField.getWindowToken(), 0);
|
||||
|
||||
mSubmit.setEnabled(false);
|
||||
mError.setText("");
|
||||
|
||||
PasswordWaiterFrag f = PasswordWaiterFrag.newInstance(oldPassword, newPassword, confirmNewPassword);
|
||||
f.setTask(new PasswordWaiter());
|
||||
f.setTargetFragment(SetPasswordFragment.this, PASSWORD_WAITER);
|
||||
mFM.beginTransaction()
|
||||
.replace(R.id.password_waiter_frag, f, PASSWORD_WAITER_TAG)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == PASSWORD_WAITER) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
Toast.makeText(getActivity(), R.string.password_changed,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
getActivity().finish();
|
||||
} else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
mSubmit.setEnabled(true);
|
||||
mError.setText(data.getStringExtra("error"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PasswordWaiterFrag extends TaskFragment<String, String, String> {
|
||||
TextView mStatus;
|
||||
|
||||
public static PasswordWaiterFrag newInstance(String... params) {
|
||||
PasswordWaiterFrag f = new PasswordWaiterFrag();
|
||||
Bundle args = new Bundle();
|
||||
args.putStringArray("params", params);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.dialog_status, container, false);
|
||||
mStatus = (TextView) v.findViewById(R.id.status);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getParams() {
|
||||
Bundle args = getArguments();
|
||||
return args.getStringArray("params");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgress(String... values) {
|
||||
mStatus.setText(values[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void taskFinished(String result) {
|
||||
super.taskFinished(result);
|
||||
|
||||
if (getTargetFragment() != null) {
|
||||
Intent i = new Intent();
|
||||
i.putExtra("result", result);
|
||||
getTargetFragment().onActivityResult(
|
||||
getTargetRequestCode(), Activity.RESULT_OK, i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void taskCancelled(String error) {
|
||||
super.taskCancelled(error);
|
||||
|
||||
if (getTargetFragment() != null) {
|
||||
Intent i = new Intent();
|
||||
i.putExtra("error", error);
|
||||
getTargetFragment().onActivityResult(
|
||||
getTargetRequestCode(), Activity.RESULT_CANCELED, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PasswordWaiter extends RobustAsyncTask<String, String, String> {
|
||||
protected String doInBackground(String... params) {
|
||||
StatusListener lsnr = new StatusListener() {
|
||||
public void updateStatus(String status) {
|
||||
publishProgress(status);
|
||||
}
|
||||
};
|
||||
try {
|
||||
I2PBote.getInstance().changePassword(
|
||||
params[0].getBytes(),
|
||||
params[1].getBytes(),
|
||||
params[2].getBytes(),
|
||||
lsnr);
|
||||
return null;
|
||||
} catch (Throwable e) {
|
||||
cancel(false);
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/i2p/bote/util/RobustAsyncTask.java
Normal file
30
src/i2p/bote/util/RobustAsyncTask.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package i2p.bote.util;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
public abstract class RobustAsyncTask<Params, Progress, Result> extends
|
||||
AsyncTask<Params, Progress, Result> {
|
||||
TaskFragment<Params, Progress, Result> mDialog;
|
||||
|
||||
void setFragment(TaskFragment<Params, Progress, Result> fragment) {
|
||||
mDialog = fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Progress... values) {
|
||||
if (mDialog != null)
|
||||
mDialog.updateProgress(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Result result) {
|
||||
if (mDialog != null)
|
||||
mDialog.taskFinished(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(Result result) {
|
||||
if (mDialog != null)
|
||||
mDialog.taskCancelled(result);
|
||||
}
|
||||
}
|
||||
69
src/i2p/bote/util/TaskFragment.java
Normal file
69
src/i2p/bote/util/TaskFragment.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package i2p.bote.util;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
|
||||
public class TaskFragment<Params, Progress, Result> extends DialogFragment {
|
||||
RobustAsyncTask<Params, Progress, Result> mTask;
|
||||
|
||||
public void setTask(RobustAsyncTask<Params, Progress, Result> task) {
|
||||
mTask = task;
|
||||
mTask.setFragment(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Retain this instance so it isn't destroyed
|
||||
setRetainInstance(true);
|
||||
|
||||
// Start the task
|
||||
if (mTask != null)
|
||||
mTask.execute(getParams());
|
||||
}
|
||||
|
||||
// This is to work around what is apparently a bug. If you don't have it
|
||||
// here the dialog will be dismissed on rotation, so tell it not to dismiss.
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
if (getDialog() != null && getRetainInstance())
|
||||
getDialog().setDismissMessage(null);
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
// This is a little hacky, but we will see if the task has finished
|
||||
// while we weren't in this activity, and then we can dismiss ourselves.
|
||||
if (mTask == null)
|
||||
dismiss();
|
||||
}
|
||||
|
||||
public Params[] getParams() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void updateProgress(Progress... values) {}
|
||||
|
||||
public void taskFinished(Result result) {
|
||||
finishTask();
|
||||
}
|
||||
|
||||
public void taskCancelled(Result result) {
|
||||
finishTask();
|
||||
}
|
||||
|
||||
private void finishTask() {
|
||||
// Make sure we check if it is resumed because we will crash if trying
|
||||
// to dismiss the dialog after the user has switched to another app.
|
||||
if (isResumed())
|
||||
dismiss();
|
||||
|
||||
// If we aren't resumed, setting the task to null will allow us to
|
||||
// dismiss ourselves in onResume().
|
||||
mTask = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user