diff --git a/README.md b/README.md index efdb25f37..74b1e1017 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ You can install this APK by: ##### Internal release +- Increase bundle_release versionCode + 1 in fastlane/Fastlane file. + - Build and publish internal release: ```bash source fastlane/.env.local && fastlane android beta diff --git a/android/app/src/main/java/network/mysterium/ui/FeedbackFragment.kt b/android/app/src/main/java/network/mysterium/ui/FeedbackFragment.kt index fa67994ac..af820a47c 100644 --- a/android/app/src/main/java/network/mysterium/ui/FeedbackFragment.kt +++ b/android/app/src/main/java/network/mysterium/ui/FeedbackFragment.kt @@ -66,6 +66,11 @@ class FeedbackFragment : Fragment() { } private fun handleFeedbackSubmit(root: View) { + if (!feedbackViewModel.isMessageSet()) { + showMessage(root.context, getString(R.string.feedback_message_required)) + return + } + feedbackSubmitButton.isEnabled = false navigateTo(root, Screen.MAIN) showMessage(root.context, getString(R.string.feedback_submit_success)) diff --git a/android/app/src/main/java/network/mysterium/ui/FeedbackViewModel.kt b/android/app/src/main/java/network/mysterium/ui/FeedbackViewModel.kt index e3b6ab190..be2beef86 100644 --- a/android/app/src/main/java/network/mysterium/ui/FeedbackViewModel.kt +++ b/android/app/src/main/java/network/mysterium/ui/FeedbackViewModel.kt @@ -53,6 +53,10 @@ class FeedbackViewModel(private val nodeRepository: NodeRepository): ViewModel() message = msg } + fun isMessageSet(): Boolean { + return message != "" + } + suspend fun submit() { val req = SendFeedbackRequest() req.description = "Platform: Android, Feedback Type: $feebackType, Message: $message" diff --git a/android/app/src/main/java/network/mysterium/ui/MainVpnFragment.kt b/android/app/src/main/java/network/mysterium/ui/MainVpnFragment.kt index 21744074c..771b3344e 100644 --- a/android/app/src/main/java/network/mysterium/ui/MainVpnFragment.kt +++ b/android/app/src/main/java/network/mysterium/ui/MainVpnFragment.kt @@ -55,6 +55,8 @@ class MainVpnFragment : Fragment() { private lateinit var vpnStatsDurationLabel: TextView private lateinit var vpnStatsBytesSentLabel: TextView private lateinit var vpnStatsBytesReceivedLabel: TextView + private lateinit var vpnStatsBytesReceivedUnits: TextView + private lateinit var vpnStatsBytesSentUnits: TextView override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -80,6 +82,8 @@ class MainVpnFragment : Fragment() { vpnStatsDurationLabel = root.findViewById(R.id.vpn_stats_duration) vpnStatsBytesReceivedLabel = root.findViewById(R.id.vpn_stats_bytes_received) vpnStatsBytesSentLabel = root.findViewById(R.id.vpn_stats_bytes_sent) + vpnStatsBytesReceivedUnits = root.findViewById(R.id.vpn_stats_bytes_received_units) + vpnStatsBytesSentUnits = root.findViewById(R.id.vpn_stats_bytes_sent_units) feedbackButton.setOnClickListener { navigateTo(root, Screen.FEEDBACK) @@ -137,8 +141,10 @@ class MainVpnFragment : Fragment() { private fun updateStatsLabels(it: StatisticsViewItem) { vpnStatsDurationLabel.text = it.duration - vpnStatsBytesReceivedLabel.text = it.bytesReceived - vpnStatsBytesSentLabel.text = it.bytesSent + vpnStatsBytesReceivedLabel.text = it.bytesReceived.value + vpnStatsBytesReceivedUnits.text = it.bytesReceived.units + vpnStatsBytesSentLabel.text = it.bytesSent.value + vpnStatsBytesSentUnits.text = it.bytesSent.units } private fun updateConnStateLabel(it: ConnectionState) { @@ -212,7 +218,7 @@ class MainVpnFragment : Fragment() { private fun connect(ctx: Context) { val proposal: ProposalViewItem? = sharedViewModel.selectedProposal.value if (proposal == null) { - showMessage(ctx, "Select proposal!") + showMessage(ctx, getString(R.string.vpn_select_proposal_warning)) return } job?.cancel() @@ -223,7 +229,7 @@ class MainVpnFragment : Fragment() { } catch (e: kotlinx.coroutines.CancellationException) { // Do nothing. } catch (e: Exception) { - showMessage(ctx, "Failed to connect. Please try again.") + showMessage(ctx, getString(R.string.vpn_failed_to_connect)) Log.e(TAG, "Failed to connect", e) } } @@ -236,7 +242,7 @@ class MainVpnFragment : Fragment() { try { sharedViewModel.disconnect() } catch (e: Exception) { - showMessage(ctx, "Failed to disconnect. Please try again.") + showMessage(ctx, getString(R.string.vpn_failed_to_disconnect)) Log.e(TAG, "Failed to disconnect", e) } } diff --git a/android/app/src/main/java/network/mysterium/ui/SharedViewModel.kt b/android/app/src/main/java/network/mysterium/ui/SharedViewModel.kt index 8dd61c4a0..1924569bb 100644 --- a/android/app/src/main/java/network/mysterium/ui/SharedViewModel.kt +++ b/android/app/src/main/java/network/mysterium/ui/SharedViewModel.kt @@ -52,8 +52,8 @@ class LocationViewItem( class StatisticsViewItem( val duration: String, - val bytesReceived: String, - val bytesSent: String + val bytesReceived: FormattedBytesViewItem, + val bytesSent: FormattedBytesViewItem ) { companion object { fun from(it: Statistics): StatisticsViewItem { @@ -196,19 +196,17 @@ class SharedViewModel( // This is needed since status change can be executed on separate // inside go node library. viewModelScope.launch { - val s = StatisticsViewItem( - duration = UnitFormatter.timeDisplay(it.duration.toDouble()), - bytesReceived = UnitFormatter.bytesDisplay(it.bytesReceived.toDouble()), - bytesSent = UnitFormatter.bytesDisplay(it.bytesSent.toDouble()) - ) - statistics.value = s + val s = StatisticsViewItem.from(it) + statistics.value = StatisticsViewItem.from(it) // Show global notification with connected country and statistics. // At this point we need to check if proposal is not null since // statistics event can fire sooner than proposal is loaded. if (selectedProposal.value != null) { val countryName = selectedProposal.value?.countryName - mysteriumCoreService.await().showNotification("Connected to $countryName", "Received ${s.bytesReceived} | Send ${s.bytesSent}") + val notificationTitle = "Connected to $countryName" + val notificationContent = "Received ${s.bytesReceived.value} ${s.bytesReceived.units} | Send ${s.bytesSent.value} ${s.bytesSent.units}" + mysteriumCoreService.await().showNotification(notificationTitle, notificationContent) } } } diff --git a/android/app/src/main/java/network/mysterium/ui/UnitFormatter.kt b/android/app/src/main/java/network/mysterium/ui/UnitFormatter.kt index d6e1d69cb..f7a70f7a3 100644 --- a/android/app/src/main/java/network/mysterium/ui/UnitFormatter.kt +++ b/android/app/src/main/java/network/mysterium/ui/UnitFormatter.kt @@ -20,17 +20,20 @@ package network.mysterium.ui import kotlin.math.floor import kotlin.math.roundToInt + +class FormattedBytesViewItem(val value: String, val units: String) + object UnitFormatter { val KB = 1024 val MB = 1024 * KB val GB = 1024 * MB - fun bytesDisplay(bytes: Double): String { + fun bytesDisplay(bytes: Double): FormattedBytesViewItem { return when { - bytes < KB -> "$bytes B" - bytes < MB -> "%.2f KB".format(bytes / KB) - bytes < GB -> "%.2f MB".format(bytes / MB) - else -> "%.2f GB".format(bytes / GB) + bytes < KB -> FormattedBytesViewItem("$bytes", "B") + bytes < MB -> FormattedBytesViewItem("%.2f".format(bytes / KB), "KB") + bytes < GB -> FormattedBytesViewItem("%.2f".format(bytes / MB), "MB") + else -> FormattedBytesViewItem("%.2f".format(bytes / GB), "GB") } } diff --git a/android/app/src/main/res/layout/fragment_main_vpn.xml b/android/app/src/main/res/layout/fragment_main_vpn.xml index 4b3b6b59c..d819a9d53 100644 --- a/android/app/src/main/res/layout/fragment_main_vpn.xml +++ b/android/app/src/main/res/layout/fragment_main_vpn.xml @@ -281,7 +281,7 @@ android:text="0" /> Loading Loading Message + Message is required. + Please select proposal to connect. + Failed to connect. Please try again. + Failed to disconnect. Please try again. diff --git a/android/app/src/test/java/UnitFormatterTest.kt b/android/app/src/test/java/UnitFormatterTest.kt index ccd0e5129..17d914b20 100644 --- a/android/app/src/test/java/UnitFormatterTest.kt +++ b/android/app/src/test/java/UnitFormatterTest.kt @@ -23,18 +23,22 @@ class UnitFormatterTest { fun testBytesFormatting() { var longVal: Long = 101 var res = UnitFormatter.bytesDisplay(longVal.toDouble()) - assertEquals("101.0 B", res) + assertEquals("101.0 B", "${res.value} ${res.units}") longVal = 1028 res = UnitFormatter.bytesDisplay(longVal.toDouble()) - assertEquals("1.00 KB", res) + assertEquals("1.00 KB", "${res.value} ${res.units}") longVal = 81068 res = UnitFormatter.bytesDisplay(longVal.toDouble()) - assertEquals("79.17 KB", res) + assertEquals("79.17 KB", "${res.value} ${res.units}") longVal = 9381068 res = UnitFormatter.bytesDisplay(longVal.toDouble()) - assertEquals("8.95 MB", res) + assertEquals("8.95 MB", "${res.value} ${res.units}") + + longVal = 1309381068 + res = UnitFormatter.bytesDisplay(longVal.toDouble()) + assertEquals("1.22 GB", "${res.value} ${res.units}") } } diff --git a/fastlane/Fastfile b/fastlane/Fastfile index f9220796e..1ecb3d56b 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -25,7 +25,7 @@ platform :android do project_dir: "android", print_command: false, # to prevent outputting passwords properties: { - 'versionCode' => 10000 + number_of_commits, + 'versionCode' => 1, 'versionName' => last_git_tag, 'applyGoogleServices' => true, "android.injected.signing.store.file" => ENV["FASTLANE_ANDROID_SIGNING_FILE_PATH"], @@ -42,7 +42,7 @@ platform :android do project_dir: "android", print_command: false, # to prevent outputting passwords properties: { - 'versionCode' => 10000 + number_of_commits, + 'versionCode' => 10694, 'versionName' => last_git_tag, 'applyGoogleServices' => true, "android.injected.signing.store.file" => ENV["FASTLANE_ANDROID_SIGNING_FILE_PATH"],