Commit 39e357a0 authored by T Dev. D's avatar T Dev. D 😎
Browse files

Merge branch 'staging' of https://code.samourai.io/wallet/samourai-wallet-android into staging

parents b07a9dee 5cb915f4
......@@ -21,7 +21,9 @@ import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.samourai.wallet.R;
import com.samourai.wallet.access.AccessFactory;
import com.samourai.wallet.api.APIFactory;
import com.samourai.wallet.cahoots.Cahoots;
import com.samourai.wallet.cahoots.psbt.PSBTUtil;
......@@ -29,11 +31,13 @@ import com.samourai.wallet.fragments.CameraFragmentBottomSheet;
import com.samourai.wallet.home.BalanceActivity;
import com.samourai.wallet.network.NetworkDashboard;
import com.samourai.wallet.network.dojo.DojoUtil;
import com.samourai.wallet.payload.PayloadUtil;
import com.samourai.wallet.send.FeeUtil;
import com.samourai.wallet.send.SendActivity;
import com.samourai.wallet.send.cahoots.ManualCahootsActivity;
import com.samourai.wallet.service.JobRefreshService;
import com.samourai.wallet.util.AppUtil;
import com.samourai.wallet.util.CharSequenceX;
import com.samourai.wallet.util.FormatsUtil;
import com.samourai.wallet.util.LinearLayoutManagerWrapper;
import com.samourai.wallet.utxos.PreSelectUtil;
......@@ -65,6 +69,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
......@@ -440,6 +445,17 @@ public class WhirlpoolMain extends AppCompatActivity {
return super.onOptionsItemSelected(item);
}
private void saveState(){
new Thread(() -> {
try {
PayloadUtil.getInstance(getApplicationContext()).saveWalletToJSON(new CharSequenceX(AccessFactory.getInstance(getApplicationContext()).getGUID() + AccessFactory.getInstance(getApplicationContext()).getPIN()));
} catch (Exception e) {
;
}
}).start();
}
private void doSCODE() {
final EditText scode = new EditText(WhirlpoolMain.this);
......@@ -453,32 +469,36 @@ public class WhirlpoolMain extends AppCompatActivity {
.setTitle(R.string.app_name)
.setMessage(R.string.enter_scode)
.setView(scode)
.setCancelable(false)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String strSCODE = scode.getText().toString().trim();
if (scode != null) {
WhirlpoolMeta.getInstance(WhirlpoolMain.this).setSCODE(strSCODE);
WhirlpoolNotificationService.stopService(getApplicationContext());
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent _intent = new Intent(WhirlpoolMain.this, BalanceActivity.class);
_intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(_intent);
}
.setNeutralButton("Remove SCODE", (dialog, which) -> {
WhirlpoolMeta.getInstance(WhirlpoolMain.this).setSCODE("");
WhirlpoolNotificationService.stopService(getApplicationContext());
saveState();
new Handler().postDelayed(() -> {
Intent _intent = new Intent(WhirlpoolMain.this, BalanceActivity.class);
_intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(_intent);
}, 1000L);
})
.setPositiveButton(R.string.ok, (dialog, whichButton) -> {
String strSCODE = scode.getText().toString().trim();
if (scode != null) {
WhirlpoolMeta.getInstance(WhirlpoolMain.this).setSCODE(strSCODE);
WhirlpoolNotificationService.stopService(getApplicationContext());
saveState();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent _intent = new Intent(WhirlpoolMain.this, BalanceActivity.class);
_intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(_intent);
}
}, 1000L);
}, 1000L);
}
dialog.dismiss();
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
}
}).show();
dialog.dismiss();
}).setNegativeButton(R.string.cancel, (dialog, whichButton) -> dialog.dismiss()).show();
}
......@@ -596,12 +616,12 @@ public class WhirlpoolMain extends AppCompatActivity {
// prefix with "Mix x/y - "
try {
int currentMix = whirlpoolUtxo.getMixsDone()+1;
int currentMix = whirlpoolUtxo.getMixsDone() + 1;
int mixsTarget = whirlpoolUtxo.getMixsTargetOrDefault(AndroidWhirlpoolWalletService.MIXS_TARGET_DEFAULT);
String mixInfo = "Mix "+currentMix+"/"+mixsTarget+" - ";
holder.mixingProgress.setText(mixInfo+holder.mixingProgress.getText());
String mixInfo = "Mix " + currentMix + "/" + mixsTarget + " - ";
holder.mixingProgress.setText(mixInfo + holder.mixingProgress.getText());
} catch (Exception ex) {
ex.printStackTrace();
ex.printStackTrace();
}
} catch (Exception ex) {
ex.printStackTrace();
......
......@@ -18,6 +18,7 @@ import android.view.animation.RotateAnimation;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.google.android.material.progressindicator.ProgressIndicator;
import com.samourai.wallet.R;
import com.samourai.wallet.whirlpool.service.WhirlpoolNotificationService;
import com.samourai.whirlpool.client.wallet.AndroidWhirlpoolWalletService;
......@@ -32,7 +33,7 @@ public class WhirlPoolLoaderDialog extends BottomSheetDialogFragment {
private static final String TAG = "WhirlPoolLoaderDialog";
private TextView statusText;
private ProgressBar statusProgress;
private ProgressIndicator statusProgress;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
private onInitComplete onInitComplete;
......@@ -78,7 +79,7 @@ public class WhirlPoolLoaderDialog extends BottomSheetDialogFragment {
case LOADING: {
new Handler().postDelayed(() -> {
statusText.setText("initializing whirlpool");
statusProgress.setProgress(30);
statusProgress.setProgressCompat(35,true);
}, 300);
break;
......@@ -87,18 +88,18 @@ public class WhirlPoolLoaderDialog extends BottomSheetDialogFragment {
case STARTING: {
new Handler().postDelayed(() -> {
statusText.setText("Connecting to service");
statusProgress.setProgress(60);
statusProgress.setProgressCompat(65,true);
}, 600);
break;
}
case CONNECTED: {
statusText.setText("Connected");
statusProgress.setProgress(100);
statusProgress.setProgressCompat(100,true);
new Handler().postDelayed(() -> {
dismiss();
if (onInitComplete != null)
onInitComplete.init();
}, 500);
}, 1200);
break;
}
......
......@@ -65,6 +65,7 @@ import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import java8.util.Optional;
import kotlin.Unit;
import static android.graphics.Typeface.BOLD;
......@@ -152,6 +153,13 @@ public class NewPoolActivity extends AppCompatActivity {
enableConfirmButton(selectedPoolViewModel != null);
});
reviewPoolFragment.setLoadingListener((aBoolean, e) -> {
if(e==null){
enableBroadcastButton(!aBoolean);
}
return Unit.INSTANCE;
});
confirmButton.setOnClickListener(view -> {
switch (newPoolViewPager.getCurrentItem()) {
case 0: {
......@@ -171,7 +179,7 @@ public class NewPoolActivity extends AppCompatActivity {
newPoolViewPager.setCurrentItem(2);
confirmButton.setText(getString(R.string.begin_cycle));
confirmButton.setBackgroundResource(R.drawable.button_green);
reviewPoolFragment.setTx0(tx0);
reviewPoolFragment.setTx0(tx0,tx0FeeTarget,selectedPoolViewModel);
break;
}
case 2: {
......@@ -494,6 +502,15 @@ public class NewPoolActivity extends AppCompatActivity {
confirmButton.setBackgroundResource(R.drawable.disabled_grey_button);
}
}
private void enableBroadcastButton(boolean enable) {
if (enable) {
confirmButton.setEnabled(true);
confirmButton.setBackgroundResource(R.drawable.button_green);
} else {
confirmButton.setEnabled(false);
confirmButton.setBackgroundResource(R.drawable.disabled_grey_button);
}
}
class NewPoolStepsPager extends FragmentPagerAdapter {
......
package com.samourai.wallet.whirlpool.newPool.fragments;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.samourai.wallet.R;
import com.samourai.wallet.whirlpool.WhirlpoolTx0;
import com.samourai.wallet.widgets.EntropyBar;
import java.text.DecimalFormat;
public class ReviewPoolFragment extends Fragment {
private static final String TAG = "SelectPoolFragment";
private EntropyBar entropyBar;
private TextView deterMinisticLinksPerTx,
totalTxs,
poolAmount,
combinationPerTxs,
poolTotalFees,
minerFees,
totalPoolAmount,
amountToCycle,
uncycledAmount,
poolFees,
entropyPerTxs;
private ProgressBar progressBar;
public ReviewPoolFragment() {
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
entropyBar = view.findViewById(R.id.pool_review_entropy_bar);
entropyPerTxs = view.findViewById(R.id.pool_review_entropy_txt);
deterMinisticLinksPerTx = view.findViewById(R.id.pool_review_deterministic_links_per_tx);
totalTxs = view.findViewById(R.id.pool_review_total_txes);
poolAmount = view.findViewById(R.id.pool_review_amount);
poolFees = view.findViewById(R.id.pool_review_pool_fee);
minerFees = view.findViewById(R.id.pool_review_miner_fee);
uncycledAmount = view.findViewById(R.id.pool_review_uncycled_amount);
amountToCycle = view.findViewById(R.id.pool_review_amount_to_cycle);
poolTotalFees = view.findViewById(R.id.pool_review_total_fees);
combinationPerTxs = view.findViewById(R.id.pool_review_combination_per_tx);
totalPoolAmount = view.findViewById(R.id.pool_review_total_pool_amount);
progressBar = view.findViewById(R.id.pool_review_progress);
entropyBar.setMaxBars(4);
entropyBar.setRange(4);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_whirlpool_review, container, false);
}
public void showProgress(boolean show) {
progressBar.setVisibility(show ? View.VISIBLE : View.GONE);
}
public void setTx0(WhirlpoolTx0 tx0) {
totalPoolAmount.setText(new DecimalFormat("0.########").format(tx0.getAmountSelected() / 1e8));
poolAmount.setText(new DecimalFormat("0.########").format(tx0.getPool() / 1e8));
poolFees.setText(new DecimalFormat("0.########").format(tx0.getFeeSamourai() / 1e8));
minerFees.setText(new DecimalFormat("0.########").format(tx0.getFee() / 1e8));
amountToCycle.setText(new DecimalFormat("0.########").format(tx0.getAmountAfterWhirlpoolFee() / 1e8));
poolTotalFees.setText(new DecimalFormat("0.########").format((tx0.getFeeSamourai() + tx0.getFee()) / 1e8));
uncycledAmount.setText(new DecimalFormat("0.########").format((tx0.getChange() / 1e8)));
totalTxs.setText(String.valueOf(tx0.getPremixRequested()));
}
}
package com.samourai.wallet.whirlpool.newPool.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.transition.AutoTransition
import androidx.transition.Fade
import androidx.transition.TransitionManager
import com.samourai.wallet.R
import com.samourai.wallet.api.backend.beans.UnspentResponse.UnspentOutput
import com.samourai.wallet.api.backend.beans.UnspentResponse.UnspentOutput.Xpub
import com.samourai.wallet.send.SendFactory
import com.samourai.wallet.util.FormatsUtil
import com.samourai.wallet.whirlpool.WhirlpoolTx0
import com.samourai.wallet.widgets.EntropyBar
import com.samourai.whirlpool.client.tx0.Tx0Preview
import com.samourai.whirlpool.client.tx0.UnspentOutputWithKey
import com.samourai.whirlpool.client.wallet.AndroidWhirlpoolWalletService
import com.samourai.whirlpool.client.wallet.beans.Tx0FeeTarget
import com.samourai.whirlpool.client.wallet.beans.WhirlpoolAccount
import com.samourai.whirlpool.client.whirlpool.beans.Pool
import kotlinx.android.synthetic.main.fragment_whirlpool_review.*
import kotlinx.coroutines.*
import org.bouncycastle.util.encoders.Hex
import java.util.*
class ReviewPoolFragment : Fragment() {
private val coroutineContext = CoroutineScope(Dispatchers.IO)
private val account = 0
private var tx0: WhirlpoolTx0? = null;
private var tx0FeeTarget: Tx0FeeTarget? = null
private var pool: Pool? = null
private var onLoading: (Boolean, Exception?) -> Unit = { _: Boolean, _: Exception? -> }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
entropyBar?.setMaxBars(4)
entropyBar?.setRange(4)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_whirlpool_review, container, false)
}
fun showProgress(show: Boolean) {
progressBar!!.visibility = if (show) View.VISIBLE else View.GONE
}
fun setLoadingListener(listener: (Boolean, Exception?) -> Unit) {
this.onLoading = listener
}
fun setTx0(tx0: WhirlpoolTx0, tx0FeeTarget: Tx0FeeTarget?, pool: Pool?) {
this.pool = pool
this.tx0 = tx0;
this.tx0FeeTarget = tx0FeeTarget
makeTxoPreview(tx0)
minerFees.text = ""
feePerUtxo.text = ""
poolFees.text = ""
uncycledAmount.text = ""
amountToCycle.text = ""
totalPoolAmount.text = ""
poolAmount.text = getBTCDisplayAmount(tx0.pool)
totalUtxoCreated.text = "${tx0.premixRequested}";
}
private fun makeTxoPreview(tx0: WhirlpoolTx0) {
showLoadingProgress(true)
onLoading.invoke(true, null)
val whirlpoolWallet = AndroidWhirlpoolWalletService.getInstance(context).whirlpoolWalletOrNull
val spendFroms: MutableCollection<UnspentOutputWithKey> = ArrayList()
for (outPoint in tx0.outpoints) {
val unspentOutput = UnspentOutput()
unspentOutput.addr = outPoint.address
unspentOutput.script = Hex.toHexString(outPoint.scriptBytes)
unspentOutput.confirmations = outPoint.confirmations
unspentOutput.tx_hash = outPoint.txHash.toString()
unspentOutput.tx_output_n = outPoint.txOutputN
unspentOutput.value = outPoint.value.value
unspentOutput.xpub = Xpub()
unspentOutput.xpub.path = "M/0/0"
val eckey = SendFactory.getPrivKey(outPoint.address, account)
val spendFrom = UnspentOutputWithKey(unspentOutput, eckey.privKeyBytes)
spendFroms.add(spendFrom)
}
coroutineContext.launch(Dispatchers.IO) {
val tx0Config = whirlpoolWallet.tx0Config
tx0Config.changeWallet = WhirlpoolAccount.DEPOSIT
try {
val poolSelected: Pool = whirlpoolWallet.poolSupplier.findPoolById(pool?.poolId)
val tx0Preview = whirlpoolWallet.tx0Preview(poolSelected, spendFroms, tx0Config, tx0FeeTarget)
withContext(Dispatchers.Main) {
showLoadingProgress(false)
setFees(tx0Preview);
onLoading.invoke(false, null);
}
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
onLoading.invoke(false, e);
showLoadingProgress(false)
}
}
}
}
private fun showLoadingProgress(loading: Boolean) {
if (loading) {
loadingFeeDetails.show()
} else {
loadingFeeDetails.hide()
}
}
private fun setFees(tx0Preview: Tx0Preview?) {
tx0Preview?.let {
TransitionManager.beginDelayedTransition(reviewLayout, Fade())
val embeddedFees = tx0Preview.premixValue.minus(tx0!!.pool);
val embeddedTotalFees = (embeddedFees * it.nbPremix)
minerFees.text = getBTCDisplayAmount(it.minerFee);
val totalFees =embeddedTotalFees + tx0Preview.feeValue + tx0Preview.minerFee;
poolTotalFees.text = getBTCDisplayAmount(totalFees);
poolFees.text = getBTCDisplayAmount(tx0Preview.feeValue)
if (it.feeDiscountPercent != 0) {
val scodeMessage = "SCODE Applied, ${it.feeDiscountPercent}% Discount"
discountText.text = scodeMessage
}
this.tx0?.let { tx0 ->
totalPoolAmount.text = "${getBTCDisplayAmount(tx0.amountSelected)}"
amountToCycle.text = getBTCDisplayAmount((tx0.amountSelected - totalFees ) - tx0Preview.changeValue )
}
uncycledAmount?.text = getBTCDisplayAmount((tx0Preview.changeValue));
feePerUtxo.text = getBTCDisplayAmount(embeddedTotalFees);
}
}
override fun onDestroy() {
coroutineContext.cancel()
super.onDestroy()
}
companion object {
private const val TAG = "SelectPoolFragment"
}
private fun getBTCDisplayAmount(value: Long): String? {
return "${FormatsUtil.getBTCDecimalFormat(value)} BTC"
}
}
\ No newline at end of file
......@@ -22,11 +22,18 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
<com.google.android.material.progressindicator.ProgressIndicator
style="@style/Widget.MaterialComponents.ProgressIndicator.Linear.Indeterminate"
android:id="@+id/whirlpool_loader_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="200dp"
android:layout_width="220dp"
android:layout_height="wrap_content"
app:indicatorCornerRadius="4dp"
app:indicatorSize="4dp"
app:trackColor="#373737"
app:growMode="bidirectional"
app:indicatorColor="?attr/colorAccent"
android:indeterminate="false"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toEndOf="@+id/imageView8"
app:layout_constraintStart_toStartOf="@+id/imageView8"
......
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:id="@+id/reviewLayout"
android:layout_height="wrap_content"
tools:context="com.samourai.wallet.whirlpool.newPool.fragments.ReviewPoolFragment">
<TextView
......@@ -15,19 +23,19 @@
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/pool_review_amount"
app:layout_constraintEnd_toStartOf="@+id/poolAmount"
app:layout_constraintHorizontal_bias="0.03"
app:layout_constraintStart_toStartOf="@+id/guideline12"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/pool_review_amount"
android:id="@+id/poolAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="0.05 BTC Pool"
tools:text="0.05 BTC Pool"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="@+id/textView51"
......@@ -40,7 +48,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:text="UTXO's CREATED"
android:text="UTXO's Created"
android:textColor="#858586"
android:typeface="monospace"
app:layout_constraintEnd_toEndOf="parent"
......@@ -49,14 +57,14 @@
app:layout_constraintTop_toBottomOf="@+id/textView51" />
<TextView
android:id="@+id/pool_review_total_txes"
android:id="@+id/totalUtxoCreated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="15"
android:textColor="#858586"
app:layout_constraintBottom_toBottomOf="@+id/textView53"
app:layout_constraintEnd_toEndOf="@+id/pool_review_amount"
app:layout_constraintEnd_toEndOf="@+id/poolAmount"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toEndOf="@+id/textView53"
app:layout_constraintTop_toTopOf="@+id/textView53" />
......@@ -65,25 +73,25 @@
android:id="@+id/textView55"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:fontFamily="monospace"
android:text="DETERMINISTIC LINKS "
android:text="Deterministic Links "
android:textColor="#858586"
app:layout_constraintEnd_toEndOf="@+id/pool_review_total_txes"
app:layout_constraintEnd_toEndOf="@+id/totalUtxoCreated"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/textView53"
app:layout_constraintTop_toBottomOf="@+id/textView53" />