You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
582 lines
24 KiB
582 lines
24 KiB
/*
|
|
* Copyright (c) 2017 m2049r
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.m2049r.xmrwallet.util;
|
|
|
|
import android.Manifest;
|
|
import android.app.Activity;
|
|
import android.app.AlertDialog;
|
|
import android.app.Dialog;
|
|
import android.content.ClipData;
|
|
import android.content.ClipboardManager;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.pm.PackageManager;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.BitmapFactory;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.drawable.BitmapDrawable;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.graphics.drawable.VectorDrawable;
|
|
import android.hardware.fingerprint.FingerprintManager;
|
|
import android.os.AsyncTask;
|
|
import android.os.Build;
|
|
import android.os.CancellationSignal;
|
|
import android.os.Environment;
|
|
import android.support.design.widget.TextInputLayout;
|
|
import android.support.v4.content.ContextCompat;
|
|
import android.system.ErrnoException;
|
|
import android.system.Os;
|
|
import android.text.Editable;
|
|
import android.text.TextWatcher;
|
|
import android.view.KeyEvent;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.WindowManager;
|
|
import android.view.animation.Animation;
|
|
import android.view.animation.AnimationUtils;
|
|
import android.view.inputmethod.EditorInfo;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
import android.widget.Button;
|
|
import android.widget.TextView;
|
|
|
|
import com.m2049r.xmrwallet.BuildConfig;
|
|
import com.m2049r.xmrwallet.R;
|
|
import com.m2049r.xmrwallet.model.NetworkType;
|
|
import com.m2049r.xmrwallet.model.Wallet;
|
|
import com.m2049r.xmrwallet.model.WalletManager;
|
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.math.BigInteger;
|
|
import java.net.MalformedURLException;
|
|
import java.net.SocketTimeoutException;
|
|
import java.net.URL;
|
|
import java.util.Locale;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
import javax.net.ssl.HttpsURLConnection;
|
|
|
|
import okhttp3.HttpUrl;
|
|
import timber.log.Timber;
|
|
|
|
public class Helper {
|
|
static public final String CRYPTO = "WOW";
|
|
|
|
static private final String WALLET_DIR = "monerujo" + (BuildConfig.DEBUG ? "-debug" : "");
|
|
static private final String HOME_DIR = "monero" + (BuildConfig.DEBUG ? "-debug" : "");
|
|
|
|
static public int DISPLAY_DIGITS_INFO = 5;
|
|
|
|
static public File getWalletRoot(Context context) {
|
|
return getStorage(context, WALLET_DIR);
|
|
}
|
|
|
|
static public File getStorage(Context context, String folderName) {
|
|
if (!isExternalStorageWritable()) {
|
|
String msg = context.getString(R.string.message_strorage_not_writable);
|
|
Timber.e(msg);
|
|
throw new IllegalStateException(msg);
|
|
}
|
|
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
|
|
if (!dir.exists()) {
|
|
Timber.i("Creating %s", dir.getAbsolutePath());
|
|
dir.mkdirs(); // try to make it
|
|
}
|
|
if (!dir.isDirectory()) {
|
|
String msg = "Directory " + dir.getAbsolutePath() + " does not exist.";
|
|
Timber.e(msg);
|
|
throw new IllegalStateException(msg);
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
static public final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
|
|
|
|
static public boolean getWritePermission(Activity context) {
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
|
if (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
== PackageManager.PERMISSION_DENIED) {
|
|
Timber.w("Permission denied to WRITE_EXTERNAL_STORAGE - requesting it");
|
|
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
|
context.requestPermissions(permissions, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static public final int PERMISSIONS_REQUEST_CAMERA = 7;
|
|
|
|
static public boolean getCameraPermission(Activity context) {
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
|
if (context.checkSelfPermission(Manifest.permission.CAMERA)
|
|
== PackageManager.PERMISSION_DENIED) {
|
|
Timber.w("Permission denied for CAMERA - requesting it");
|
|
String[] permissions = {Manifest.permission.CAMERA};
|
|
context.requestPermissions(permissions, PERMISSIONS_REQUEST_CAMERA);
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static public File getWalletFile(Context context, String aWalletName) {
|
|
File walletDir = getWalletRoot(context);
|
|
File f = new File(walletDir, aWalletName);
|
|
Timber.d("wallet=%s size= %d", f.getAbsolutePath(), f.length());
|
|
return f;
|
|
}
|
|
|
|
/* Checks if external storage is available for read and write */
|
|
private static boolean isExternalStorageWritable() {
|
|
String state = Environment.getExternalStorageState();
|
|
return Environment.MEDIA_MOUNTED.equals(state);
|
|
}
|
|
|
|
static public void showKeyboard(Activity act) {
|
|
InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
imm.showSoftInput(act.getCurrentFocus(), InputMethodManager.SHOW_IMPLICIT);
|
|
}
|
|
|
|
static public void hideKeyboard(Activity act) {
|
|
if (act == null) return;
|
|
if (act.getCurrentFocus() == null) {
|
|
act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
|
|
} else {
|
|
InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
imm.hideSoftInputFromWindow((null == act.getCurrentFocus()) ? null : act.getCurrentFocus().getWindowToken(),
|
|
InputMethodManager.HIDE_NOT_ALWAYS);
|
|
}
|
|
}
|
|
|
|
static public void showKeyboard(Dialog dialog) {
|
|
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
|
}
|
|
|
|
static public void hideKeyboardAlways(Activity act) {
|
|
act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
|
}
|
|
|
|
static public String getDisplayAmount(long amount) {
|
|
return getDisplayAmount(amount, 12);
|
|
}
|
|
|
|
static public String getDisplayAmount(long amount, int maxDecimals) {
|
|
return getDisplayAmount(Wallet.getDisplayAmount(amount), maxDecimals);
|
|
}
|
|
|
|
// amountString must have '.' as decimal point
|
|
private static String getDisplayAmount(String amountString, int maxDecimals) {
|
|
int lastZero = 0;
|
|
int decimal = 0;
|
|
for (int i = amountString.length() - 1; i >= 0; i--) {
|
|
if ((lastZero == 0) && (amountString.charAt(i) != '0')) lastZero = i + 1;
|
|
// TODO i18n
|
|
if (amountString.charAt(i) == '.') {
|
|
decimal = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
int cutoff = Math.min(Math.max(lastZero, decimal + 2), decimal + maxDecimals);
|
|
return amountString.substring(0, cutoff);
|
|
}
|
|
|
|
static public String getFormattedAmount(double amount, boolean isXmr) {
|
|
// at this point selection is XMR in case of error
|
|
String displayB;
|
|
if (isXmr) { // XMR
|
|
long xmr = Wallet.getAmountFromDouble(amount);
|
|
if ((xmr > 0) || (amount == 0)) {
|
|
displayB = String.format(Locale.US, "%,.5f", amount);
|
|
} else {
|
|
displayB = null;
|
|
}
|
|
} else { // not XMR
|
|
displayB = String.format(Locale.US, "%,.2f", amount);
|
|
}
|
|
return displayB;
|
|
}
|
|
|
|
static public Bitmap getBitmap(Context context, int drawableId) {
|
|
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
|
|
if (drawable instanceof BitmapDrawable) {
|
|
return BitmapFactory.decodeResource(context.getResources(), drawableId);
|
|
} else if (drawable instanceof VectorDrawable) {
|
|
return getBitmap((VectorDrawable) drawable);
|
|
} else {
|
|
throw new IllegalArgumentException("unsupported drawable type");
|
|
}
|
|
}
|
|
|
|
static private Bitmap getBitmap(VectorDrawable vectorDrawable) {
|
|
Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
|
|
vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
|
Canvas canvas = new Canvas(bitmap);
|
|
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
|
vectorDrawable.draw(canvas);
|
|
return bitmap;
|
|
}
|
|
|
|
static final int HTTP_TIMEOUT = 5000;
|
|
|
|
static public String getUrl(String httpsUrl) {
|
|
HttpsURLConnection urlConnection = null;
|
|
try {
|
|
URL url = new URL(httpsUrl);
|
|
urlConnection = (HttpsURLConnection) url.openConnection();
|
|
urlConnection.setConnectTimeout(HTTP_TIMEOUT);
|
|
urlConnection.setReadTimeout(HTTP_TIMEOUT);
|
|
InputStreamReader in = new InputStreamReader(urlConnection.getInputStream());
|
|
StringBuffer sb = new StringBuffer();
|
|
final int BUFFER_SIZE = 512;
|
|
char[] buffer = new char[BUFFER_SIZE];
|
|
int length = in.read(buffer, 0, BUFFER_SIZE);
|
|
while (length >= 0) {
|
|
sb.append(buffer, 0, length);
|
|
length = in.read(buffer, 0, BUFFER_SIZE);
|
|
}
|
|
return sb.toString();
|
|
} catch (SocketTimeoutException ex) {
|
|
Timber.w("C %s", ex.getLocalizedMessage());
|
|
} catch (MalformedURLException ex) {
|
|
Timber.e("A %s", ex.getLocalizedMessage());
|
|
} catch (IOException ex) {
|
|
Timber.e("B %s", ex.getLocalizedMessage());
|
|
} finally {
|
|
if (urlConnection != null) {
|
|
urlConnection.disconnect();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static public void clipBoardCopy(Context context, String label, String text) {
|
|
ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
ClipData clip = ClipData.newPlainText(label, text);
|
|
clipboardManager.setPrimaryClip(clip);
|
|
}
|
|
|
|
static private Animation ShakeAnimation;
|
|
|
|
static public Animation getShakeAnimation(Context context) {
|
|
if (ShakeAnimation == null) {
|
|
synchronized (Helper.class) {
|
|
if (ShakeAnimation == null) {
|
|
ShakeAnimation = AnimationUtils.loadAnimation(context, R.anim.shake);
|
|
}
|
|
}
|
|
}
|
|
return ShakeAnimation;
|
|
}
|
|
|
|
static public HttpUrl getXmrToBaseUrl() {
|
|
if ((WalletManager.getInstance() == null)
|
|
|| (WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet)) {
|
|
return HttpUrl.parse("https://test.xmr.to/api/v2/xmr2btc/");
|
|
} else {
|
|
return HttpUrl.parse("https://xmr.to/api/v2/xmr2btc/");
|
|
}
|
|
}
|
|
|
|
private final static char[] HexArray = "0123456789ABCDEF".toCharArray();
|
|
|
|
public static String bytesToHex(byte[] data) {
|
|
if ((data != null) && (data.length > 0))
|
|
return String.format("%0" + (data.length * 2) + "X", new BigInteger(1, data));
|
|
else return "";
|
|
}
|
|
|
|
public static byte[] hexToBytes(String hex) {
|
|
final int len = hex.length();
|
|
final byte[] data = new byte[len / 2];
|
|
for (int i = 0; i < len; i += 2) {
|
|
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
|
|
+ Character.digit(hex.charAt(i + 1), 16));
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static public void setMoneroHome(Context context) {
|
|
try {
|
|
String home = getStorage(context, HOME_DIR).getAbsolutePath();
|
|
Os.setenv("HOME", home, true);
|
|
} catch (ErrnoException ex) {
|
|
throw new IllegalStateException(ex);
|
|
}
|
|
}
|
|
|
|
static public void initLogger(Context context) {
|
|
if (BuildConfig.DEBUG) {
|
|
initLogger(context, WalletManager.LOGLEVEL_DEBUG);
|
|
}
|
|
// no logger if not debug
|
|
}
|
|
|
|
// TODO make the log levels refer to the WalletManagerFactory::LogLevel enum ?
|
|
static public void initLogger(Context context, int level) {
|
|
String home = getStorage(context, HOME_DIR).getAbsolutePath();
|
|
WalletManager.initLogger(home + "/monerujo", "monerujo.log");
|
|
if (level >= WalletManager.LOGLEVEL_SILENT)
|
|
WalletManager.setLogLevel(level);
|
|
}
|
|
|
|
// try to figure out what the real wallet password is given the user password
|
|
// which could be the actual wallet password or a (maybe malformed) CrAzYpass
|
|
// or the password used to derive the CrAzYpass for the wallet
|
|
static public String getWalletPassword(Context context, String walletName, String password) {
|
|
String walletPath = new File(getWalletRoot(context), walletName + ".keys").getAbsolutePath();
|
|
|
|
// try with entered password (which could be a legacy password or a CrAzYpass)
|
|
if (WalletManager.getInstance().verifyWalletPassword(walletPath, password, true)) {
|
|
return password;
|
|
}
|
|
|
|
// maybe this is a malformed CrAzYpass?
|
|
String possibleCrazyPass = CrazyPassEncoder.reformat(password);
|
|
if (possibleCrazyPass != null) { // looks like a CrAzYpass
|
|
if (WalletManager.getInstance().verifyWalletPassword(walletPath, possibleCrazyPass, true)) {
|
|
return possibleCrazyPass;
|
|
}
|
|
}
|
|
|
|
// generate & try with CrAzYpass
|
|
String crazyPass = KeyStoreHelper.getCrazyPass(context, password);
|
|
if (WalletManager.getInstance().verifyWalletPassword(walletPath, crazyPass, true)) {
|
|
return crazyPass;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
static AlertDialog openDialog = null; // for preventing opening of multiple dialogs
|
|
static AsyncTask<Void, Void, Boolean> loginTask = null;
|
|
|
|
static public void promptPassword(final Context context, final String wallet, boolean fingerprintDisabled, final PasswordAction action) {
|
|
if (openDialog != null) return; // we are already asking for password
|
|
LayoutInflater li = LayoutInflater.from(context);
|
|
final View promptsView = li.inflate(R.layout.prompt_password, null);
|
|
|
|
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
|
|
alertDialogBuilder.setView(promptsView);
|
|
|
|
final TextInputLayout etPassword = (TextInputLayout) promptsView.findViewById(R.id.etPassword);
|
|
etPassword.setHint(context.getString(R.string.prompt_password, wallet));
|
|
|
|
final TextView tvOpenPrompt = (TextView) promptsView.findViewById(R.id.tvOpenPrompt);
|
|
final Drawable icFingerprint = context.getDrawable(R.drawable.ic_fingerprint);
|
|
final Drawable icError = context.getDrawable(R.drawable.ic_error_red_36dp);
|
|
final Drawable icInfo = context.getDrawable(R.drawable.ic_info_green_36dp);
|
|
|
|
final boolean fingerprintAuthCheck = FingerprintHelper.isFingerPassValid(context, wallet);
|
|
|
|
final boolean fingerprintAuthAllowed = !fingerprintDisabled && fingerprintAuthCheck;
|
|
final CancellationSignal cancelSignal = new CancellationSignal();
|
|
|
|
final AtomicBoolean incorrectSavedPass = new AtomicBoolean(false);
|
|
class LoginWalletTask extends AsyncTask<Void, Void, Boolean> {
|
|
private String pass;
|
|
private boolean fingerprintUsed;
|
|
|
|
LoginWalletTask(String pass, boolean fingerprintUsed) {
|
|
this.pass = pass;
|
|
this.fingerprintUsed = fingerprintUsed;
|
|
}
|
|
|
|
@Override
|
|
protected void onPreExecute() {
|
|
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icInfo, null, null, null);
|
|
tvOpenPrompt.setText(context.getText(R.string.prompt_open_wallet));
|
|
tvOpenPrompt.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
@Override
|
|
protected Boolean doInBackground(Void... unused) {
|
|
return processPasswordEntry(context, wallet, pass, fingerprintUsed, action);
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(Boolean result) {
|
|
if (result) {
|
|
Helper.hideKeyboardAlways((Activity) context);
|
|
cancelSignal.cancel();
|
|
openDialog.dismiss();
|
|
openDialog = null;
|
|
} else {
|
|
if (fingerprintUsed) {
|
|
incorrectSavedPass.set(true);
|
|
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icError, null, null, null);
|
|
tvOpenPrompt.setText(context.getText(R.string.bad_saved_password));
|
|
} else {
|
|
if (!fingerprintAuthAllowed) {
|
|
tvOpenPrompt.setVisibility(View.GONE);
|
|
} else if (incorrectSavedPass.get()) {
|
|
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icError, null, null, null);
|
|
tvOpenPrompt.setText(context.getText(R.string.bad_password));
|
|
} else {
|
|
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icFingerprint, null, null, null);
|
|
tvOpenPrompt.setText(context.getText(R.string.prompt_fingerprint_auth));
|
|
}
|
|
etPassword.setError(context.getString(R.string.bad_password));
|
|
}
|
|
}
|
|
|
|
loginTask = null;
|
|
}
|
|
}
|
|
|
|
etPassword.getEditText().addTextChangedListener(new TextWatcher() {
|
|
|
|
@Override
|
|
public void afterTextChanged(Editable s) {
|
|
if (etPassword.getError() != null) {
|
|
etPassword.setError(null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void beforeTextChanged(CharSequence s, int start,
|
|
int count, int after) {
|
|
}
|
|
|
|
@Override
|
|
public void onTextChanged(CharSequence s, int start,
|
|
int before, int count) {
|
|
}
|
|
});
|
|
|
|
// set dialog message
|
|
alertDialogBuilder
|
|
.setCancelable(false)
|
|
.setPositiveButton(context.getString(R.string.label_ok), null)
|
|
.setNegativeButton(context.getString(R.string.label_cancel),
|
|
new DialogInterface.OnClickListener() {
|
|
public void onClick(DialogInterface dialog, int id) {
|
|
Helper.hideKeyboardAlways((Activity) context);
|
|
cancelSignal.cancel();
|
|
if (loginTask != null) {
|
|
loginTask.cancel(true);
|
|
loginTask = null;
|
|
}
|
|
dialog.cancel();
|
|
openDialog = null;
|
|
}
|
|
});
|
|
openDialog = alertDialogBuilder.create();
|
|
|
|
final FingerprintManager.AuthenticationCallback fingerprintAuthCallback;
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
fingerprintAuthCallback = null;
|
|
} else {
|
|
fingerprintAuthCallback = new FingerprintManager.AuthenticationCallback() {
|
|
@Override
|
|
public void onAuthenticationError(int errMsgId, CharSequence errString) {
|
|
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icError, null, null, null);
|
|
tvOpenPrompt.setText(errString);
|
|
}
|
|
|
|
@Override
|
|
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
|
try {
|
|
String userPass = KeyStoreHelper.loadWalletUserPass(context, wallet);
|
|
if (loginTask == null) {
|
|
loginTask = new LoginWalletTask(userPass, true);
|
|
loginTask.execute();
|
|
}
|
|
} catch (KeyStoreHelper.BrokenPasswordStoreException ex) {
|
|
etPassword.setError(context.getString(R.string.bad_password));
|
|
// TODO: better error message here - what would it be?
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAuthenticationFailed() {
|
|
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icError, null, null, null);
|
|
tvOpenPrompt.setText(context.getString(R.string.bad_fingerprint));
|
|
}
|
|
};
|
|
}
|
|
|
|
openDialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
|
@Override
|
|
public void onShow(DialogInterface dialog) {
|
|
if (fingerprintAuthAllowed && fingerprintAuthCallback != null) {
|
|
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icFingerprint, null, null, null);
|
|
tvOpenPrompt.setText(context.getText(R.string.prompt_fingerprint_auth));
|
|
tvOpenPrompt.setVisibility(View.VISIBLE);
|
|
FingerprintHelper.authenticate(context, cancelSignal, fingerprintAuthCallback);
|
|
}
|
|
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
|
button.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View view) {
|
|
String pass = etPassword.getEditText().getText().toString();
|
|
if (loginTask == null) {
|
|
loginTask = new LoginWalletTask(pass, false);
|
|
loginTask.execute();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// accept keyboard "ok"
|
|
etPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
|
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
|
|
String pass = etPassword.getEditText().getText().toString();
|
|
if (loginTask == null) {
|
|
loginTask = new LoginWalletTask(pass, false);
|
|
loginTask.execute();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
|
|
Helper.showKeyboard(openDialog);
|
|
openDialog.show();
|
|
}
|
|
|
|
public interface PasswordAction {
|
|
void action(String walletName, String password, boolean fingerprintUsed);
|
|
}
|
|
|
|
static private boolean processPasswordEntry(Context context, String walletName, String pass, boolean fingerprintUsed, PasswordAction action) {
|
|
String walletPassword = Helper.getWalletPassword(context, walletName, pass);
|
|
if (walletPassword != null) {
|
|
action.action(walletName, walletPassword, fingerprintUsed);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static public ExchangeApi getExchangeApi() {
|
|
return new com.m2049r.xmrwallet.service.exchange.coinmarketcap.ExchangeApiImpl(OkHttpClientSingleton.getOkHttpClient());
|
|
|
|
}
|
|
}
|