Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
@@ -0,0 +1,106 @@
package com.brainwallet.data.repository

import android.content.SharedPreferences
import androidx.core.content.edit
import com.brainwallet.data.source.AnalyticsSource
import com.brainwallet.data.source.PeerManagerSource
import org.koin.core.annotation.Single
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.UUID

@Single
class SyncAnalyticsRepository(
private val analyticsSource: AnalyticsSource,
private val peerManagerSource: PeerManagerSource,
private val prefs: SharedPreferences,
) {
fun startSync() {
// Record the start time of the current sync segment.
prefs.edit {
putLong(KEY_CURRENT_SYNC_START_TIMESTAMP, peerManagerSource.getLastBlockTimestamp())
}
}

fun stopSync() {
val start = prefs.getLong(KEY_CURRENT_SYNC_START_TIMESTAMP, 0L)
if (start != 0L) {
val duration = peerManagerSource.getLastBlockTimestamp() - start
val accumulated = prefs.getLong(KEY_ACCUMULATED_DURATION, 0L)
prefs.edit {
putLong(KEY_ACCUMULATED_DURATION, accumulated + duration)
remove(KEY_CURRENT_SYNC_START_TIMESTAMP)
}
}
}

fun completeSync() {
// Capture the duration of the final segment.
stopSync()

val totalDuration = prefs.getLong(KEY_ACCUMULATED_DURATION, 0L)
if (totalDuration == 0L) return

val endTimestamp = peerManagerSource.getLastBlockTimestamp()
val endBlockHeight = peerManagerSource.getCurrentBlockHeight()
val uuid = UUID.randomUUID().toString()
val totalDurationInMillis = totalDuration * 1000

prefs.edit {
putString(KEY_LAST_UUID, uuid)
putLong(KEY_LAST_DURATION, totalDurationInMillis)
putLong(KEY_LAST_END, endTimestamp)
remove(KEY_ACCUMULATED_DURATION)
}

val params = mapOf(
KEY_UUID to uuid,
KEY_DURATION_MILLIS to (totalDurationInMillis),
KEY_END_TIMESTAMP to endTimestamp,
KEY_END_BLOCK_HEIGHT to endBlockHeight
)
analyticsSource.logEventWithParams(KEY_EVENT, params)
}

fun getLastSyncMetadata(): SyncMetadata? {
val uuid = prefs.getString(KEY_LAST_UUID, null) ?: return null
val duration = prefs.getLong(KEY_LAST_DURATION, 0L)
val end = prefs.getLong(KEY_LAST_END, 0L)
return SyncMetadata(uuid, duration, end)
}

data class SyncMetadata(
val uuid: String,
val durationMillis: Long,
val endTimestamp: Long
) {
class Formatter(
private val dateFormat: SimpleDateFormat = SimpleDateFormat(
"MMMM dd, yyyy h:mm:ss a",
Locale.getDefault()
)
) {
fun format(syncMetadata: SyncMetadata): String {
val durationSeconds = syncMetadata.durationMillis / 1000.0
val date = Date(syncMetadata.endTimestamp * 1000)
val dateString = dateFormat.format(date)

return "Duration: %.1f seconds\nTimestamp: %s".format(durationSeconds, dateString)
}
}
}

companion object {
private const val KEY_CURRENT_SYNC_START_TIMESTAMP = "current_sync_start_timestamp"
private const val KEY_ACCUMULATED_DURATION = "accumulated_sync_duration"
private const val KEY_LAST_UUID = "last_sync_uuid"
private const val KEY_LAST_DURATION = "last_sync_duration"
private const val KEY_LAST_END = "last_sync_end_timestamp"
private const val KEY_DURATION_MILLIS = "duration_millis"
private const val KEY_END_TIMESTAMP = "end_timestamp"
private const val KEY_END_BLOCK_HEIGHT = "end_block_height"
private const val KEY_UUID = "uuid"
private const val KEY_EVENT = "user_did_complete_sync"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.brainwallet.data.source

interface AnalyticsSource {
fun logEvent(event: String)
fun logEventWithParams(event: String, params: Map<String, Any?>)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.brainwallet.data.source

import android.content.Context
import android.os.Bundle
import com.google.firebase.analytics.FirebaseAnalytics
import org.koin.core.annotation.Single

@Single
class FirebaseAnalyticsSource(
private val context: Context,
private val analytics: FirebaseAnalytics = FirebaseAnalytics.getInstance(context)
) : AnalyticsSource {

override fun logEvent(event: String) {
analytics.logEvent(event, null)
}

override fun logEventWithParams(event: String, params: Map<String, Any?>) {
val bundle = Bundle().apply {
params.forEach { (key, value) ->
when (value) {
is String -> putString(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Double -> putDouble(key, value)
is Boolean -> putBoolean(key, value)
// add more types if needed
}
}
}
analytics.logEvent(event, bundle)
}
}
17 changes: 17 additions & 0 deletions app/src/main/java/com/brainwallet/data/source/PeerManagerSource.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.brainwallet.data.source

import com.brainwallet.wallet.BRPeerManager
import org.koin.core.annotation.Single

interface BRPeerManagerProxy {
fun getCurrentBlockHeight(): Int
fun getLastBlockTimestamp(): Long
}

@Single
class PeerManagerSource(
private val proxy: BRPeerManagerProxy = object : BRPeerManagerProxy {
override fun getCurrentBlockHeight(): Int = BRPeerManager.getCurrentBlockHeight()
override fun getLastBlockTimestamp(): Long = BRPeerManager.getInstance().lastBlockTimestamp
}
) : BRPeerManagerProxy by proxy
13 changes: 12 additions & 1 deletion app/src/main/java/com/brainwallet/tools/manager/SyncManager.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.brainwallet.tools.manager;

import static com.brainwallet.data.source.RemoteConfigSource.KEY_FEATURE_SELECTED_PEERS_ENABLED;
import static com.brainwallet.tools.manager.BRSharedPrefs.putSyncMetadata;

import android.app.AlarmManager;
Expand All @@ -9,13 +10,17 @@
import android.os.Build;
import android.os.Bundle;

import com.brainwallet.data.repository.SyncAnalyticsRepository;
import com.brainwallet.data.source.RemoteConfigSource;
import com.brainwallet.tools.listeners.SyncReceiver;
import com.brainwallet.tools.util.BRConstants;
import com.brainwallet.tools.util.Utils;
import com.brainwallet.R;
import com.brainwallet.presenter.activities.BreadActivity;
import com.brainwallet.wallet.BRPeerManager;

import org.koin.java.KoinJavaComponent;

import java.util.concurrent.TimeUnit;

import timber.log.Timber;
Expand All @@ -34,6 +39,10 @@ public static SyncManager getInstance() {
private SyncManager() {
}

public static SyncAnalyticsRepository getSyncAnalyticsRepository() {
return KoinJavaComponent.get(SyncAnalyticsRepository.class);
}

public synchronized void startSyncingProgressThread(Context app) {
try {
if (syncTask != null) {
Expand All @@ -45,6 +54,7 @@ public synchronized void startSyncingProgressThread(Context app) {
syncTask = null;
}
syncTask = new SyncProgressTask();
getSyncAnalyticsRepository().startSync();
syncTask.start();
updateStartSyncData(app);
} catch (IllegalThreadStateException ex) {
Expand All @@ -59,10 +69,11 @@ private synchronized void updateStartSyncData(Context app) {
private synchronized void markFinishedSyncData(Context app) {
Timber.d("timber: || SYNC ELAPSE markFinish threadname:%s", Thread.currentThread().getName());
final double progress = BRPeerManager.syncProgress(BRSharedPrefs.getStartHeight(app));
getSyncAnalyticsRepository().completeSync();
}

public synchronized void stopSyncingProgressThread(Context app) {

getSyncAnalyticsRepository().stopSync();
if (app == null) {
Timber.i("timber: || stopSyncingProgressThread: ctx is null");
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package com.brainwallet.ui.screens.home
import com.brainwallet.data.model.CurrencyEntity
import com.brainwallet.data.model.Language

import com.brainwallet.data.repository.SyncAnalyticsRepository

sealed class SettingsEvent {
data class OnLoad(
val shareAnalyticsDataEnabled: Boolean = false,
val lastSyncMetadata: String? = null,
val lastSyncMetadata: SyncAnalyticsRepository.SyncMetadata? = null,
) : SettingsEvent()
object OnSecurityUpdatePinClick : SettingsEvent()
object OnSecuritySeedPhraseClick : SettingsEvent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import com.brainwallet.data.model.Language
import com.brainwallet.data.model.toFeeOptions
import com.brainwallet.tools.manager.FeeManager

import com.brainwallet.data.repository.SyncAnalyticsRepository

data class SettingsState(
val darkMode: Boolean = true,
val selectedLanguage: Language = Language.ENGLISH,
Expand All @@ -19,7 +21,7 @@ data class SettingsState(
val languageSelectorBottomSheetVisible: Boolean = false,
val fiatSelectorBottomSheetVisible: Boolean = false,
val shareAnalyticsDataEnabled: Boolean = false,
val lastSyncMetadata: String? = null,
val lastSyncMetadata: SyncAnalyticsRepository.SyncMetadata? = null,
val currentFeeOptions: List<FeeOption> = Fee.Default.toFeeOptions(),
val selectedFeeType: String = FeeManager.LUXURY
)
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import androidx.lifecycle.coroutineScope
import androidx.lifecycle.findViewTreeLifecycleOwner
import com.brainwallet.R
import com.brainwallet.data.model.AppSetting
import com.brainwallet.data.repository.SyncAnalyticsRepository
import com.brainwallet.tools.manager.BRSharedPrefs
import com.brainwallet.tools.util.BRConstants
import com.brainwallet.ui.screens.home.SettingsEvent
Expand All @@ -48,20 +49,22 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import org.koin.compose.koinInject
import org.koin.compose.viewmodel.koinViewModel
import org.koin.java.KoinJavaComponent.inject

@Composable
fun HomeSettingDrawerSheet(
modifier: Modifier = Modifier,
viewModel: SettingsViewModel = koinInject()
syncAnalyticsRepository: SyncAnalyticsRepository = koinInject(),
viewModel: SettingsViewModel = koinViewModel()
) {
val state by viewModel.state.collectAsState()
val context = LocalContext.current

LaunchedEffect(Unit) {
viewModel.onEvent(SettingsEvent.OnLoad(
shareAnalyticsDataEnabled = BRSharedPrefs.getShareData(context), //currently just load analytics share data here
lastSyncMetadata = BRSharedPrefs.getSyncMetadata(context), //currently just load sync metadata here
lastSyncMetadata = syncAnalyticsRepository.getLastSyncMetadata(), //currently just load sync metadata here
))
}

Expand Down Expand Up @@ -192,10 +195,15 @@ fun HomeSettingDrawerSheet(
}

item {
val description = state.lastSyncMetadata?.let {
SyncAnalyticsRepository.SyncMetadata.Formatter()
.format(it)
} ?: "No sync metadata"

SettingRowItem(
modifier = Modifier.height(100.dp),
title = stringResource(R.string.settings_title_sync_metadata),
description = state.lastSyncMetadata ?: "No sync metadata"
description = description
)
}

Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/com/brainwallet/util/TimeProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.brainwallet.util

import org.koin.core.annotation.Single

@Single
class TimeProvider(
private val nowGetter: () -> Long = { System.currentTimeMillis() }
) {
fun now(): Long {
return nowGetter()
}
}
Loading