package i2p.bote.android.identities;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import i2p.bote.I2PBote;
import i2p.bote.android.R;
import i2p.bote.android.util.RobustAsyncTask;
import i2p.bote.android.util.TaskFragment;
import i2p.bote.fileencryption.PasswordException;
public abstract class IdentityShipFragment extends Fragment {
private Callbacks mCallbacks = sDummyCallbacks;
public interface Callbacks {
public void onTaskFinished();
}
private static Callbacks sDummyCallbacks = new Callbacks() {
public void onTaskFinished() {}
};
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof Callbacks))
throw new IllegalStateException("Activity must implement fragment's callbacks.");
mCallbacks = (Callbacks) activity;
}
@Override
public void onDetach() {
super.onDetach();
mCallbacks = sDummyCallbacks;
}
// Code to identify the fragment that is calling onActivityResult().
static final int SHIP_WAITER = 0;
// Tag so we can find the task fragment again, in another
// instance of this fragment after rotation.
static final String SHIP_WAITER_TAG = "shipWaiterTask";
TextView mError;
public static IdentityShipFragment newInstance(boolean exporting) {
return exporting ?
new ExportIdentitiesFragment() :
new ImportIdentitiesFragment();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
ShipWaiterFrag f = (ShipWaiterFrag) getFragmentManager().findFragmentByTag(SHIP_WAITER_TAG);
if (f != null)
f.setTargetFragment(this, SHIP_WAITER);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mError = (TextView) view.findViewById(R.id.error);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getActivity().setTitle(getTitle());
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SHIP_WAITER) {
if (resultCode == Activity.RESULT_OK) {
mCallbacks.onTaskFinished();
} else if (resultCode == Activity.RESULT_CANCELED) {
setInterfaceEnabled(true);
mError.setText(data.getStringExtra("error"));
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
protected abstract int getTitle();
protected abstract void setInterfaceEnabled(boolean enabled);
public static class ShipWaiterFrag extends TaskFragment<Object, String, String> {
String currentStatus;
TextView mStatus;
@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);
if (currentStatus != null && !currentStatus.isEmpty())
mStatus.setText(currentStatus);
return v;
}
@Override
public void updateProgress(String... values) {
currentStatus = values[0];
mStatus.setText(currentStatus);
}
@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);
}
}
}
public static class ExportIdentitiesFragment extends IdentityShipFragment {
EditText mExportFilename;
TextView mSuffix;
CheckBox mEncrypt;
View mPasswordEntry;
EditText mPassword;
EditText mConfirmPassword;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_export_identities, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mExportFilename = (EditText) view.findViewById(R.id.export_filename);
mSuffix = (TextView) view.findViewById(R.id.suffix);
mEncrypt = (CheckBox) view.findViewById(R.id.encrypt);
mPasswordEntry = view.findViewById(R.id.password_entry);
mPassword = (EditText) view.findViewById(R.id.password);
mConfirmPassword = (EditText) view.findViewById(R.id.password_confirm);
mEncrypt.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
mSuffix.setText(b ? ".bote" : ".txt");
mPasswordEntry.setVisibility(b ? View.VISIBLE : View.GONE);
}
});
view.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String exportFilename = mExportFilename.getText().toString();
String suffix = mSuffix.getText().toString();
boolean encrypt = mEncrypt.isChecked();
String password = null;
if (encrypt) {
password = mPassword.getText().toString();
String confirmPassword = mConfirmPassword.getText().toString();
if (password.isEmpty()) {
mPassword.setError(getActivity().getString(R.string.this_field_is_required));
mPassword.requestFocus();
return;
} else
mPassword.setError(null);
if (confirmPassword.isEmpty()) {
mConfirmPassword.setError(getActivity().getString(R.string.this_field_is_required));
mConfirmPassword.requestFocus();
return;
} else if (!password.equals(confirmPassword)) {
mConfirmPassword.setError(getActivity().getString(R.string.passwords_do_not_match));
mConfirmPassword.requestFocus();
return;
} else
mConfirmPassword.setError(null);
}
File exportFile = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS
), exportFilename + suffix);
if (exportFile.exists()) {
// TODO ask to rename or overwrite
mExportFilename.setError(getActivity().getString(R.string.file_exists));
mExportFilename.requestFocus();
return;
} else
mExportFilename.setError(null);
exportIdentities(exportFile, password);
}
});
}
private void exportIdentities(File exportFile, String password) {
setInterfaceEnabled(false);
mError.setText("");
ExportWaiterFrag f = ExportWaiterFrag.newInstance(exportFile, password);
f.setTask(new ExportWaiter());
f.setTargetFragment(ExportIdentitiesFragment.this, SHIP_WAITER);
getFragmentManager().beginTransaction()
.replace(R.id.waiter_frag, f, SHIP_WAITER_TAG)
.commit();
}
@Override
protected int getTitle() {
return R.string.export_identities;
}
@Override
protected void setInterfaceEnabled(boolean enabled) {
mExportFilename.setEnabled(enabled);
mEncrypt.setEnabled(enabled);
mPassword.setEnabled(enabled);
mConfirmPassword.setEnabled(enabled);
}
public static class ExportWaiterFrag extends ShipWaiterFrag {
static final String SHIP_FILE = "shipFile";
static final String PASSWORD = "password";
public static ExportWaiterFrag newInstance(File shipFile, String password) {
ExportWaiterFrag f = new ExportWaiterFrag();
Bundle args = new Bundle();
args.putSerializable(SHIP_FILE, shipFile);
args.putString(PASSWORD, password);
f.setArguments(args);
return f;
}
@Override
public Object[] getParams() {
Bundle args = getArguments();
return new Object[]{
args.getSerializable(SHIP_FILE),
args.getString(PASSWORD),
};
}
}
private class ExportWaiter extends RobustAsyncTask<Object, String, String> {
@Override
protected String doInBackground(Object... params) {
try {
publishProgress(getResources().getString(R.string.exporting_identities));
I2PBote.getInstance().getIdentities().export(
(File) params[0],
(String) params[1]);
return null;
} catch (Throwable e) {
cancel(false);
return e.getMessage();
}
}
}
}
public static class ImportIdentitiesFragment extends IdentityShipFragment {
static final int SELECT_IMPORT_FILE = 1;
EditText mPassword;
CheckBox mOverwrite;
CheckBox mReplace;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_import_identities, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mPassword = (EditText) view.findViewById(R.id.password);
mOverwrite = (CheckBox) view.findViewById(R.id.overwrite);
mReplace = (CheckBox) view.findViewById(R.id.replace);
mOverwrite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
mReplace.setVisibility(b ? View.GONE : View.VISIBLE);
}
});
view.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("text/plain");
i.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(i, SELECT_IMPORT_FILE);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(getActivity(), R.string.please_install_a_file_manager,
Toast.LENGTH_SHORT).show();
}
}
});
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SELECT_IMPORT_FILE) {
if (resultCode == Activity.RESULT_OK) {
Uri result = data.getData();
try {
ParcelFileDescriptor pfd = getActivity().getContentResolver().openFileDescriptor(result, "r");
String password = mPassword.getText().toString();
if (password.isEmpty())
password = null;
importIdentities(pfd, password, !mOverwrite.isChecked(), mReplace.isChecked());
} catch (FileNotFoundException e) {
e.printStackTrace();
mError.setText(e.getLocalizedMessage());
}
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void importIdentities(ParcelFileDescriptor importFile, String password,
boolean append, boolean replace) {
setInterfaceEnabled(false);
mError.setText("");
ImportWaiterFrag f = ImportWaiterFrag.newInstance(
importFile, password, append, replace);
f.setTask(new ImportWaiter());
f.setTargetFragment(ImportIdentitiesFragment.this, SHIP_WAITER);
getFragmentManager().beginTransaction()
.replace(R.id.waiter_frag, f, SHIP_WAITER_TAG)
.commit();
}
@Override
protected int getTitle() {
return R.string.import_identities;
}
@Override
protected void setInterfaceEnabled(boolean enabled) {
mPassword.setEnabled(enabled);
mOverwrite.setEnabled(enabled);
}
public static class ImportWaiterFrag extends ShipWaiterFrag {
static final String SHIP_FILE_DESCRIPTOR = "shipFile";
static final String PASSWORD = "password";
static final String APPEND = "append";
static final String REPLACE = "replace";
public static ImportWaiterFrag newInstance(ParcelFileDescriptor shipFile, String password,
boolean append, boolean replace) {
ImportWaiterFrag f = new ImportWaiterFrag();
Bundle args = new Bundle();
args.putParcelable(SHIP_FILE_DESCRIPTOR, shipFile);
args.putString(PASSWORD, password);
args.putBoolean(APPEND, append);
args.putBoolean(REPLACE, replace);
f.setArguments(args);
return f;
}
@Override
public Object[] getParams() {
Bundle args = getArguments();
return new Object[]{
((ParcelFileDescriptor) args.getParcelable(SHIP_FILE_DESCRIPTOR))
.getFileDescriptor(),
args.getString(PASSWORD),
args.getBoolean(APPEND),
args.getBoolean(REPLACE),
};
}
}
private class ImportWaiter extends RobustAsyncTask<Object, String, String> {
@Override
protected String doInBackground(Object... params) {
try {
publishProgress(getResources().getString(R.string.importing_identities));
boolean success = I2PBote.getInstance().getIdentities().importFromFileDescriptor(
(FileDescriptor) params[0],
(String) params[1],
(Boolean) params[2],
(Boolean) params[3]);
if (success)
return null;
else {
cancel(false);
return (params[1] == null) ?
getResources().getString(R.string.no_identities_found_maybe_encrypted) :
getResources().getString(R.string.no_identities_found);
}
} catch (Throwable e) {
e.printStackTrace();
cancel(false);
if (e instanceof PasswordException)
return getResources().getString(R.string.password_incorrect);
return e.getLocalizedMessage();
}
}
}
}
}