Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(dashpay): improve progress UI for mixing and otherfixes timeskew progress #1261

Merged
merged 4 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
Comment on lines +1 to +10
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we had a circular progress indicator, but couldn't find it. I remember adding it to Coinbase.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-02-28 at 8 09 28 AM

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
4 changes: 3 additions & 1 deletion wallet/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -517,10 +517,12 @@
<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_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
Loading