xmrwallet/app/src/main/java/com/m2049r/xmrwallet/ledger/LedgerProgressDialog.java

183 lines
7.9 KiB
Java

/*
* Copyright (c) 2018 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.ledger;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.dialog.ProgressDialog;
import timber.log.Timber;
public class LedgerProgressDialog extends ProgressDialog implements Ledger.Listener {
static public final int TYPE_DEBUG = 0;
static public final int TYPE_RESTORE = 1;
static public final int TYPE_SUBADDRESS = 2;
static public final int TYPE_ACCOUNT = 3;
static public final int TYPE_SEND = 4;
private final int type;
private Handler uiHandler = new Handler(Looper.getMainLooper());
public LedgerProgressDialog(Context context, int type) {
super(context);
this.type = type;
setCancelable(false);
if (type == TYPE_SEND)
setMessage(context.getString(R.string.info_prepare_tx));
else
setMessage(context.getString(R.string.progress_ledger_progress));
}
@Override
public void onBackPressed() {
// prevent back button
}
private int firstSubaddress = Integer.MAX_VALUE;
private boolean validate = false;
private boolean validated = false;
@Override
public void onInstructionSend(final Instruction ins, final byte[] apdu) {
Timber.d("LedgerProgressDialog SEND %s", ins);
uiHandler.post(new Runnable() {
@Override
public void run() {
if (type > TYPE_DEBUG) {
validate = false;
switch (ins) {
case INS_RESET: // ledger may ask for confirmation - maybe a bug?
case INS_GET_KEY: // ledger asks for confirmation to send keys
case INS_DISPLAY_ADDRESS:
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_confirm));
break;
case INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY: // lookahead
//00 4a 00 00 09 00 01000000 30000000
// 0 1 2 3 4 5 6 7 8 9 a b c d
int account = bytesToInteger(apdu, 6);
int subaddress = bytesToInteger(apdu, 10);
Timber.d("fetching subaddress (%d, %d)", account, subaddress);
switch (type) {
case TYPE_RESTORE:
setProgress(account * Ledger.LOOKAHEAD_SUBADDRESSES + subaddress + 1,
Ledger.LOOKAHEAD_ACCOUNTS * Ledger.LOOKAHEAD_SUBADDRESSES);
setIndeterminate(false);
break;
case TYPE_ACCOUNT:
final int requestedSubaddress = account * Ledger.LOOKAHEAD_SUBADDRESSES + subaddress;
if (firstSubaddress > requestedSubaddress) {
firstSubaddress = requestedSubaddress;
}
setProgress(requestedSubaddress - firstSubaddress + 1,
Ledger.LOOKAHEAD_ACCOUNTS * Ledger.LOOKAHEAD_SUBADDRESSES);
setIndeterminate(false);
break;
case TYPE_SUBADDRESS:
if (firstSubaddress > subaddress) {
firstSubaddress = subaddress;
}
setProgress(subaddress - firstSubaddress + 1, Ledger.LOOKAHEAD_SUBADDRESSES);
setIndeterminate(false);
break;
default:
setIndeterminate(true);
break;
}
setMessage(getContext().getString(R.string.progress_ledger_lookahead));
break;
case INS_VERIFY_KEY:
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_verify));
break;
case INS_OPEN_TX:
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_opentx));
break;
case INS_MLSAG:
if (validated) {
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_mlsag));
}
break;
case INS_PREFIX_HASH:
if ((apdu[2] != 1) || (apdu[3] != 0)) break;
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_confirm));
break;
case INS_VALIDATE:
if ((apdu[2] != 1) || (apdu[3] != 1)) break;
validate = true;
uiHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (validate) {
setIndeterminate(true);
setMessage(getContext().getString(R.string.progress_ledger_confirm));
validated = true;
}
}
}, 250);
break;
default:
// ignore others and maintain state
}
} else {
setMessage(ins.name());
}
}
});
}
@Override
public void onInstructionReceive(final Instruction ins, final byte[] data) {
Timber.d("LedgerProgressDialog RECV %s", ins);
uiHandler.post(new Runnable() {
@Override
public void run() {
if (type > TYPE_DEBUG) {
switch (ins) {
case INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY: // lookahead
case INS_VERIFY_KEY:
case INS_GET_CHACHA8_PREKEY:
break;
default:
if (type != TYPE_SEND)
setMessage(getContext().getString(R.string.progress_ledger_progress));
}
} else {
setMessage("Returned from " + ins.name());
}
}
});
}
// TODO: we use ints in Java but the are signed; accounts & subaddresses are unsigned ...
private int bytesToInteger(byte[] bytes, int offset) {
int result = 0;
for (int i = 3; i >= 0; i--) {
result <<= 8;
result |= (bytes[offset + i] & 0xFF);
}
return result;
}
}