Skip to content

Commit

Permalink
fix(dashpay): improve progress UI for mixing and other fixes (#1261)
Browse files Browse the repository at this point in the history
* feat: add more mixing progress indicators

* fix: improve TimeSkew function

* fix: keep mixing process alive
  • Loading branch information
HashEngineering authored Mar 12, 2024
1 parent 6e30797 commit e8b9a18
Show file tree
Hide file tree
Showing 20 changed files with 323 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ class PaymentHeaderView @JvmOverloads constructor(
binding.paymentAddressViewTitle.text = title
}

fun setBalanceTitle(title: String) {
binding.paymentAddressViewBalanceTitle.text = title
}

fun setProposition(title: String) {
binding.paymentAddressViewProposition.text = title
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/circular_progress_indicator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"/>
10 changes: 10 additions & 0 deletions common/src/main/res/drawable/circular_progress_indicator.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportWidth="12"
android:viewportHeight="12">
<path
android:pathData="M6.416,1.314C5.561,1.238 4.702,1.398 3.931,1.775C3.16,2.153 2.507,2.734 2.043,3.456C1.579,4.178 1.321,5.013 1.298,5.871C1.274,6.729 1.486,7.577 1.909,8.323L0.783,8.963C0.242,8.011 -0.028,6.929 0.002,5.835C0.032,4.741 0.361,3.676 0.953,2.755C1.545,1.834 2.378,1.093 3.361,0.611C4.344,0.13 5.44,-0.073 6.531,0.024L6.416,1.314Z"
android:fillColor="#008DE4"
android:fillType="evenOdd"/>
</vector>
2 changes: 2 additions & 0 deletions common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,6 @@
<string name="enter_amount_available">available</string>
<string name="transaction_row_rate_not_available">Not available</string>
<string name="log_in">Log In</string>
<!-- basic formats -->
<string name="percent" translatable="false">%d%%</string>
</resources>
4 changes: 2 additions & 2 deletions wallet/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ android {
compileSdk 33
minSdkVersion 23
targetSdkVersion 33
versionCode project.hasProperty('versionCode') ? project.property('versionCode') as int : 90000
versionName project.hasProperty('versionName') ? project.property('versionName') : "5.3-dashpay"
versionCode project.hasProperty('versionCode') ? project.property('versionCode') as int : 90002
versionName project.hasProperty('versionName') ? project.property('versionName') : "5.4-dashpay"
multiDexEnabled true
generatedDensities = ['hdpi', 'xhdpi']
vectorDrawables.useSupportLibrary = true
Expand Down
44 changes: 38 additions & 6 deletions wallet/res/layout/activity_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,29 +130,61 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />

<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="12dp"
android:layout_height="12dp"
android:paddingEnd="5dp"
style="@style/Widget.AppCompat.ProgressBar"
app:layout_constraintStart_toStartOf="@id/coinjoin_title"
app:layout_constraintTop_toTopOf="@id/coinjoin_subtitle"
app:layout_constraintBottom_toBottomOf="@id/coinjoin_subtitle"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/animated_circular_progress_indictator"/>

<TextView
android:id="@+id/coinjoin_subtitle"
style="@style/Overline.Tertiary"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginStart="22dp"
android:text="@string/turned_off"
app:layout_constraintStart_toEndOf="@id/coinjoin_icon"
app:layout_constraintStart_toEndOf="@id/progress_bar"
app:layout_constraintTop_toBottomOf="@id/coinjoin_title"
app:layout_constraintBottom_toBottomOf="parent" />

<TextView
android:id="@+id/coinjoin_progress"
style="@style/Overline.Tertiary"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
app:layout_constraintStart_toEndOf="@id/coinjoin_subtitle"
app:layout_constraintEnd_toStartOf="@id/balance"
app:layout_constraintBottom_toBottomOf="@id/coinjoin_subtitle"
tools:text="(10%)"/>

<TextView
android:id="@+id/balance"
style="@style/Overline.Tertiary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="3dp"
app:layout_constraintEnd_toStartOf="@id/coinjoin_subtitle_icon"
app:layout_constraintTop_toBottomOf="@id/coinjoin_title"
app:layout_constraintBottom_toBottomOf="@id/coinjoin_subtitle"
tools:text="0.012 of 0.028" />

<ImageView
android:id="@+id/coinjoin_subtitle_icon"
android:layout_gravity="center_vertical"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="3dp"
android:layout_marginTop="3dp"
android:visibility="gone"
android:layout_marginStart="5dp"
android:layout_marginEnd="15dp"
app:srcCompat="@drawable/ic_dash"
app:tint="@color/content_tertiary"
app:layout_constraintTop_toTopOf="@id/coinjoin_subtitle"
app:layout_constraintStart_toEndOf="@id/coinjoin_subtitle" />
app:layout_constraintBottom_toBottomOf="@id/coinjoin_subtitle"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.constraintlayout.widget.ConstraintLayout
Expand Down
36 changes: 35 additions & 1 deletion wallet/res/layout/mixing_status_pane.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,50 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="5dp"
style="@style/Widget.AppCompat.ProgressBar"
app:layout_constraintStart_toEndOf="@id/mixing_icon"
app:layout_constraintTop_toTopOf="@id/mixing_mode"
app:layout_constraintBottom_toBottomOf="@id/mixing_mode"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/animated_circular_progress_indictator"/>


<TextView
android:id="@+id/mixing_mode"
style="@style/Overline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@string/coinjoin_mixing"
app:layout_constraintStart_toEndOf="@id/mixing_icon"
app:layout_constraintStart_toEndOf="@id/progress_bar"
app:layout_constraintTop_toTopOf="@id/mixing_icon" />

<TextView
android:id="@+id/mixing_sessions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/mixing_mode"
app:layout_constraintTop_toTopOf="@id/mixing_mode"
tools:text="...."
/>

<TextView
android:id="@+id/mixing_percent"
style="@style/Overline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="3dp"
app:layout_constraintStart_toEndOf="@+id/mixing_mode"
app:layout_constraintEnd_toStartOf="@id/balance"
app:layout_constraintTop_toTopOf="@id/mixing_mode"
tools:text="50%" />

<TextView
android:id="@+id/balance"
style="@style/Overline"
Expand Down
5 changes: 4 additions & 1 deletion wallet/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -517,10 +517,13 @@
<string name="coinjoin_start">Start Mixing</string>
<string name="coinjoin_stop">Stop Mixing</string>
<string name="coinjoin_mixing">Mixing</string>
<string name="coinjoin_progress">Mixing · %1$s of %2$s</string>
<string name="coinjoin_paused">Mixing Paused</string>
<string name="coinjoin_not_started">Not Started</string>
<string name="coinjoin_progress">%s (%d%%) %s of %s</string>
<string name="coinjoin_progress_balance">%1$s of %2$s</string>
<string name="coinjoin_progress_finished">Fully Mixed</string>
<string name="coinjoin_change_level_confirmation">Are you sure you want to change the privacy level?</string>
<string name="coinjoin_stop_mixing_title">Are you sure you want to stop mixing?</string>
<string name="coinjoin_stop_mixing_message">Any funds that have been mixed will be combined with your unmixed funds</string>
<string name="coinjoin_mixed_balance">Mixed balance:</string>
</resources>
70 changes: 51 additions & 19 deletions wallet/src/de/schildbach/wallet/service/BlockchainServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,6 @@
import de.schildbach.wallet.util.CrashReporter;
import de.schildbach.wallet.util.ThrottlingWalletChangeListener;
import de.schildbach.wallet_test.R;
import kotlin.Unit;
import kotlin.coroutines.Continuation;
import kotlinx.coroutines.flow.FlowCollector;

import static org.dash.wallet.common.util.Constants.PREFIX_ALMOST_EQUAL_TO;

Expand Down Expand Up @@ -224,7 +221,8 @@ public class BlockchainServiceImpl extends LifecycleService implements Blockchai
private Executor executor = Executors.newSingleThreadExecutor();
private int syncPercentage = 0; // 0 to 100%
private MixingStatus mixingStatus = MixingStatus.NOT_STARTED;
private boolean isForegroundService = false;
private Double mixingProgress = 0.0;
private ForegroundService foregroundService = ForegroundService.NONE;

// Risk Analyser for Transactions that is PeerGroup Aware
AllowLockTimeRiskAnalysis.Analyzer riskAnalyzer;
Expand Down Expand Up @@ -1042,25 +1040,46 @@ public void onCreate() {

updateAppWidget();
FlowExtKt.observe(blockchainStateDao.observeState(), this, (blockchainState, continuation) -> {
handleBlockchainStateNotification((BlockchainState) blockchainState, mixingStatus);
handleBlockchainStateNotification(blockchainState, mixingStatus, mixingProgress);
return null;
});
registerCrowdNodeConfirmedAddressFilter();

FlowExtKt.observe(coinJoinService.observeMixingState(), this, (mixingStatus, continuation) -> {
handleBlockchainStateNotification(blockchainState, mixingStatus);
handleBlockchainStateNotification(blockchainState, mixingStatus, mixingProgress);
return null;
});

FlowExtKt.observe(coinJoinService.observeMixingProgress(), this, (mixingProgress, continuation) -> {
handleBlockchainStateNotification(blockchainState, mixingStatus, mixingProgress);
return null;
});
}

private Notification createCoinJoinNotification(Coin mixedBalance, Coin totalBalance) {
private Notification createCoinJoinNotification() {
Coin mixedBalance = ((WalletEx)application.getWallet()).getCoinJoinBalance();
Coin totalBalance = application.getWallet().getBalance();
Intent notificationIntent = OnboardingActivity.createIntent(this);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

DecimalFormat decimalFormat = new DecimalFormat("0.000");
int statusStringId = R.string.error;
switch(mixingStatus) {
case MIXING:
statusStringId = R.string.coinjoin_mixing;
break;
case PAUSED:
statusStringId = R.string.coinjoin_paused;
break;
case FINISHED:
statusStringId = R.string.coinjoin_progress_finished;
break;
}
final String message = getString(
R.string.coinjoin_progress,
getString(statusStringId),
mixingProgress.intValue(),
decimalFormat.format(MonetaryExtKt.toBigDecimal(mixedBalance)),
decimalFormat.format(MonetaryExtKt.toBigDecimal(totalBalance))
);
Expand Down Expand Up @@ -1156,7 +1175,15 @@ private void startForeground() {
//preventing it from being killed in Android 26 or later
Notification notification = createNetworkSyncNotification(null);
startForeground(Constants.NOTIFICATION_ID_BLOCKCHAIN_SYNC, notification);
isForegroundService = true;
foregroundService = ForegroundService.BLOCKCHAIN_SYNC;
}

private void startForegroundCoinJoin() {
// Shows ongoing notification promoting service to foreground service and
// preventing it from being killed in Android 26 or later
Notification notification = createCoinJoinNotification();
startForeground(Constants.NOTIFICATION_ID_BLOCKCHAIN_SYNC, notification);
foregroundService = ForegroundService.COINJOIN_MIXING;
}

@Override
Expand Down Expand Up @@ -1308,34 +1335,39 @@ private void broadcastPeerState(final int numPeers) {
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
}

private void handleBlockchainStateNotification(BlockchainState blockchainState, MixingStatus mixingStatus) {
private void handleBlockchainStateNotification(BlockchainState blockchainState, MixingStatus mixingStatus, double mixingProgress) {
// send this out for the Network Monitor, other activities observe the database
final Intent broadcast = new Intent(ACTION_BLOCKCHAIN_STATE);
broadcast.setPackage(getPackageName());
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);

log.info("handle blockchain state notification: {}, {}", foregroundService, mixingStatus);
this.mixingProgress = mixingProgress;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && blockchainState != null
&& blockchainState.getBestChainDate() != null) {
//Handle Ongoing notification state
boolean syncing = blockchainState.getBestChainDate().getTime() < (Utils.currentTimeMillis() - DateUtils.HOUR_IN_MILLIS); //1 hour
if (!syncing && blockchainState.getBestChainHeight() == config.getBestChainHeightEver() && mixingStatus != MixingStatus.MIXING) {
//Remove ongoing notification if blockchain sync finished
stopForeground(true);
isForegroundService = false;
foregroundService = ForegroundService.NONE;
nm.cancel(Constants.NOTIFICATION_ID_BLOCKCHAIN_SYNC);
} else if (blockchainState.getReplaying() || syncing) {
//Shows ongoing notification when synchronizing the blockchain
Notification notification = createNetworkSyncNotification(blockchainState);
nm.notify(Constants.NOTIFICATION_ID_BLOCKCHAIN_SYNC, notification);
} else if (mixingStatus == MixingStatus.MIXING) {
Notification notification = createCoinJoinNotification(
((WalletEx)application.getWallet()).getCoinJoinBalance(),
application.getWallet().getBalance()
);
if (isForegroundService) {
nm.notify(Constants.NOTIFICATION_ID_BLOCKCHAIN_SYNC, notification);
} else if (mixingStatus == MixingStatus.MIXING || mixingStatus == MixingStatus.PAUSED) {
log.info("foreground service: {}", foregroundService);
if (foregroundService == ForegroundService.NONE) {
log.info("foreground service not active, create notification");
startForegroundCoinJoin();
//Notification notification = createCoinJoinNotification();
//nm.notify(Constants.NOTIFICATION_ID_BLOCKCHAIN_SYNC, notification);
foregroundService = ForegroundService.COINJOIN_MIXING;
} else {
startForeground();
log.info("foreground service active, update notification");
Notification notification = createCoinJoinNotification();
//nm.cancel(Constants.NOTIFICATION_ID_BLOCKCHAIN_SYNC);
nm.notify(Constants.NOTIFICATION_ID_BLOCKCHAIN_SYNC, notification);
}
}
}
Expand Down
Loading

0 comments on commit e8b9a18

Please sign in to comment.