diff --git a/android/app/build.gradle b/android/app/build.gradle
index f9d292809..3fd671b9b 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -71,7 +71,7 @@ android {
}
defaultConfig {
- applicationId "network.mysterium.vpn"
+ applicationId "network.mysterium.provider"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode getVersionCode()
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 9620cbb77..a82e1bfdf 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -90,6 +90,10 @@
android:name="updated.mysterium.vpn.ui.settings.SettingsActivity"
android:screenOrientation="portrait" />
+
+
diff --git a/android/app/src/main/java/updated/mysterium/vpn/core/DeferredNode.kt b/android/app/src/main/java/updated/mysterium/vpn/core/DeferredNode.kt
index 6cd30fafb..6fb52c35e 100644
--- a/android/app/src/main/java/updated/mysterium/vpn/core/DeferredNode.kt
+++ b/android/app/src/main/java/updated/mysterium/vpn/core/DeferredNode.kt
@@ -8,7 +8,6 @@ import mysterium.MobileNode
// DeferredNode is a wrapper class which holds MobileNode instance promise.
// This allows to load UI without waiting for node to start.
class DeferredNode {
-
private companion object {
const val TAG = "DeferredNode"
}
@@ -26,6 +25,7 @@ class DeferredNode {
) {
if (!lock.tryAcquire()) {
Log.i(TAG, "Node is already started or starting, skipping")
+
} else {
val handler = CoroutineExceptionHandler { _, exception ->
Log.e(TAG, exception.localizedMessage ?: exception.toString())
diff --git a/android/app/src/main/java/updated/mysterium/vpn/core/MysteriumAndroidCoreService.kt b/android/app/src/main/java/updated/mysterium/vpn/core/MysteriumAndroidCoreService.kt
index 4f0e658b9..87a1a052e 100644
--- a/android/app/src/main/java/updated/mysterium/vpn/core/MysteriumAndroidCoreService.kt
+++ b/android/app/src/main/java/updated/mysterium/vpn/core/MysteriumAndroidCoreService.kt
@@ -25,12 +25,10 @@ import android.os.Binder
import android.os.Bundle
import android.os.IBinder
import android.util.Log
-import kotlinx.coroutines.CoroutineExceptionHandler
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.*
import mysterium.MobileNode
import mysterium.Mysterium
+import network.mysterium.vpn.BuildConfig
import network.mysterium.vpn.R
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@@ -75,6 +73,7 @@ class MysteriumAndroidCoreService : VpnService(), KoinComponent {
private var currentState = ConnectionState.NOTCONNECTED
private var vpnTimeSpent: Float? = null // time spent for last session in minutes
private var secondsBetweenAnalyticEvent = 0
+ private var isProviderActive = false
override fun onDestroy() {
stopMobileNode()
@@ -89,11 +88,50 @@ class MysteriumAndroidCoreService : VpnService(), KoinComponent {
return MysteriumCoreServiceBridge()
}
+ private fun startMobileProviderService(active: Boolean) {
+ mobileNode?.let {
+ isProviderActive = active
+ try {
+ if (active) {
+ it.startProvider()
+ } else {
+ it.stopProvider()
+ }
+
+ } catch (e: Exception) {
+ isProviderActive = !active
+ println(e)
+ }
+ }
+ }
+
+ private fun innerStopConsumer() {
+ var c = currentState == ConnectionState.CONNECTED ||
+ currentState == ConnectionState.CONNECTING ||
+ currentState == ConnectionState.ON_HOLD ||
+ currentState == ConnectionState.IP_NOT_CHANGED
+
+
+ GlobalScope.launch(Dispatchers.IO) {
+ if (c) {
+ connectionUseCase.disconnect()
+
+ activeProposal = null
+ deferredNode = null
+ stopForeground(true)
+ }
+ }
+ }
+
private fun startMobileNode(filesPath: String): MobileNode {
mobileNode?.let {
return it
}
- mobileNode = Mysterium.newNode(filesPath, Mysterium.defaultNodeOptions())
+ mobileNode = Mysterium.newNode(filesPath, Mysterium.defaultProviderNodeOptions())
+
+ val launcherVersion = String.format("%s/android", BuildConfig.VERSION_NAME)
+ Mysterium.setFlagLauncherVersion(launcherVersion)
+
mobileNode?.overrideWireguardConnection(WireguardAndroidTunnelSetup(this@MysteriumAndroidCoreService))
return mobileNode ?: MobileNode()
}
@@ -272,6 +310,16 @@ class MysteriumAndroidCoreService : VpnService(), KoinComponent {
inner class MysteriumCoreServiceBridge : Binder(), MysteriumCoreService {
+ override fun stopConsumer() {
+ innerStopConsumer()
+ }
+ override fun startProvider(active: Boolean) {
+ startMobileProviderService(active)
+ }
+ override fun isProviderActive(): Boolean {
+ return isProviderActive
+ }
+
override fun getDeferredNode() = deferredNode
override fun setDeferredNode(node: DeferredNode?) {
diff --git a/android/app/src/main/java/updated/mysterium/vpn/core/MysteriumCoreService.kt b/android/app/src/main/java/updated/mysterium/vpn/core/MysteriumCoreService.kt
index 4f1e98ed3..4be88f539 100644
--- a/android/app/src/main/java/updated/mysterium/vpn/core/MysteriumCoreService.kt
+++ b/android/app/src/main/java/updated/mysterium/vpn/core/MysteriumCoreService.kt
@@ -26,6 +26,9 @@ import updated.mysterium.vpn.notification.NotificationFactory
interface MysteriumCoreService : IBinder {
suspend fun startNode(): MobileNode
+ fun isProviderActive(): Boolean
+ fun startProvider(provider: Boolean)
+ fun stopConsumer()
fun stopNode()
diff --git a/android/app/src/main/java/updated/mysterium/vpn/core/NodeRepository.kt b/android/app/src/main/java/updated/mysterium/vpn/core/NodeRepository.kt
index 14f9916d9..4cd5e98f0 100644
--- a/android/app/src/main/java/updated/mysterium/vpn/core/NodeRepository.kt
+++ b/android/app/src/main/java/updated/mysterium/vpn/core/NodeRepository.kt
@@ -19,6 +19,7 @@ import updated.mysterium.vpn.model.identity.MigrateHermesStatus
import updated.mysterium.vpn.model.identity.MigrateHermesStatusResponse
import updated.mysterium.vpn.model.manual.connect.ConnectionState
import updated.mysterium.vpn.model.manual.connect.CountryInfo
+import updated.mysterium.vpn.model.manual.connect.ServiceStatus
import updated.mysterium.vpn.model.nodes.ProposalItem
import updated.mysterium.vpn.model.nodes.ProposalsResponse
import updated.mysterium.vpn.model.payment.Order
@@ -71,6 +72,16 @@ class NodeRepository(var deferredNode: DeferredNode) {
}
}
+ // Register service status callback.
+ suspend fun registerServiceStatusChangeCallback(cb: (stats: ServiceStatus) -> Unit) {
+ withContext(Dispatchers.IO) {
+ deferredNode.await()
+ .registerServiceStatusChangeCallback { service, status ->
+ cb(ServiceStatus(service, status))
+ }
+ }
+ }
+
// Register statistics callback.
suspend fun registerStatisticsChangeCallback(cb: (stats: Statistics) -> Unit) {
withContext(Dispatchers.IO) {
diff --git a/android/app/src/main/java/updated/mysterium/vpn/di/Modules.kt b/android/app/src/main/java/updated/mysterium/vpn/di/Modules.kt
index 7ae92ddd1..91fb460cd 100644
--- a/android/app/src/main/java/updated/mysterium/vpn/di/Modules.kt
+++ b/android/app/src/main/java/updated/mysterium/vpn/di/Modules.kt
@@ -31,6 +31,7 @@ import updated.mysterium.vpn.ui.onboarding.OnboardingViewModel
import updated.mysterium.vpn.ui.prepare.top.up.PrepareTopUpViewModel
import updated.mysterium.vpn.ui.private.key.PrivateKeyViewModel
import updated.mysterium.vpn.ui.profile.ProfileViewModel
+import updated.mysterium.vpn.ui.provider.ProviderViewModel
import updated.mysterium.vpn.ui.report.issue.ReportIssueViewModel
import updated.mysterium.vpn.ui.search.SearchViewModel
import updated.mysterium.vpn.ui.settings.SettingsViewModel
@@ -158,6 +159,9 @@ object Modules {
viewModel {
SelectCountryViewModel(get())
}
+ single {
+ ProviderViewModel(get())
+ }
}
fun provideDatabase(context: Context) = Room.databaseBuilder(
diff --git a/android/app/src/main/java/updated/mysterium/vpn/model/manual/connect/ProviderState.kt b/android/app/src/main/java/updated/mysterium/vpn/model/manual/connect/ProviderState.kt
new file mode 100644
index 000000000..e8a3f58c5
--- /dev/null
+++ b/android/app/src/main/java/updated/mysterium/vpn/model/manual/connect/ProviderState.kt
@@ -0,0 +1,10 @@
+package updated.mysterium.vpn.model.manual.connect
+
+data class ProviderState(
+ val active: Boolean,
+)
+
+class ServiceStatus(
+ val service: String,
+ val status: String
+)
diff --git a/android/app/src/main/java/updated/mysterium/vpn/model/notification/NotificationChannels.kt b/android/app/src/main/java/updated/mysterium/vpn/model/notification/NotificationChannels.kt
index 8d0853d73..581a0cac6 100644
--- a/android/app/src/main/java/updated/mysterium/vpn/model/notification/NotificationChannels.kt
+++ b/android/app/src/main/java/updated/mysterium/vpn/model/notification/NotificationChannels.kt
@@ -7,4 +7,5 @@ object NotificationChannels {
const val BALANCE_NOTIFICATION_ID = 4
const val PRIVATE_KEY_NOTIFICATION_ID = 5
const val PAYMENT_STATUS_ID = 6
+ const val PROVIDER_NOTIFICATION = 7
}
diff --git a/android/app/src/main/java/updated/mysterium/vpn/network/usecase/ConnectionUseCase.kt b/android/app/src/main/java/updated/mysterium/vpn/network/usecase/ConnectionUseCase.kt
index f1a917cec..322ebf0ff 100644
--- a/android/app/src/main/java/updated/mysterium/vpn/network/usecase/ConnectionUseCase.kt
+++ b/android/app/src/main/java/updated/mysterium/vpn/network/usecase/ConnectionUseCase.kt
@@ -3,10 +3,13 @@ package updated.mysterium.vpn.network.usecase
import mysterium.ConnectRequest
import mysterium.GetIdentityRequest
import mysterium.RegisterIdentityRequest
+import okhttp3.internal.format
import updated.mysterium.vpn.core.DeferredNode
import updated.mysterium.vpn.core.NodeRepository
import updated.mysterium.vpn.database.preferences.SharedPreferencesList
import updated.mysterium.vpn.database.preferences.SharedPreferencesManager
+import updated.mysterium.vpn.model.manual.connect.ConnectionState
+import updated.mysterium.vpn.model.manual.connect.ServiceStatus
import updated.mysterium.vpn.model.statistics.Statistics
import updated.mysterium.vpn.model.wallet.Identity
@@ -77,6 +80,10 @@ class ConnectionUseCase(
callback: (Statistics) -> Unit
) = nodeRepository.registerStatisticsChangeCallback(callback)
+ suspend fun serviceStatusChangeCallback(
+ callback: (ServiceStatus) -> Unit
+ ) = nodeRepository.registerServiceStatusChangeCallback(callback)
+
suspend fun connectionStatusCallback(
callback: (String) -> Unit
) = nodeRepository.registerConnectionStatusChangeCallback(callback)
diff --git a/android/app/src/main/java/updated/mysterium/vpn/notification/AppNotificationManager.kt b/android/app/src/main/java/updated/mysterium/vpn/notification/AppNotificationManager.kt
index ab4bc2823..c23b5afe2 100644
--- a/android/app/src/main/java/updated/mysterium/vpn/notification/AppNotificationManager.kt
+++ b/android/app/src/main/java/updated/mysterium/vpn/notification/AppNotificationManager.kt
@@ -8,6 +8,7 @@ import androidx.core.app.NotificationCompat
import network.mysterium.vpn.R
import updated.mysterium.vpn.model.notification.NotificationChannels
import updated.mysterium.vpn.ui.connection.ConnectionActivity
+import updated.mysterium.vpn.ui.provider.ProviderActivity
import updated.mysterium.vpn.ui.splash.SplashActivity
import updated.mysterium.vpn.ui.splash.SplashActivity.Companion.REDIRECTED_FROM_PUSH_KEY
@@ -19,6 +20,7 @@ class AppNotificationManager(private val notificationManager: NotificationManage
const val ACTION_DISCONNECT = "DISCONNECT"
}
+ private val providerChannel = "provider"
private val statisticsChannel = "statistics"
private val connLostChannel = "connectionlost"
private val paymentStatusChannel = "paymentstatus"
@@ -28,6 +30,7 @@ class AppNotificationManager(private val notificationManager: NotificationManage
// pendingAppIntent is used to navigate back to MainActivity
// when user taps on notification.
private lateinit var pendingAppIntent: PendingIntent
+ private lateinit var pendingProviderIntent: PendingIntent
fun init(ctx: Context) {
context = ctx
@@ -36,11 +39,17 @@ class AppNotificationManager(private val notificationManager: NotificationManage
}
pendingAppIntent = PendingIntent.getActivity(ctx, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ val intentProvider = Intent(ctx, ProviderActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
+ }
+ pendingProviderIntent = PendingIntent.getActivity(ctx, 0, intentProvider, PendingIntent.FLAG_IMMUTABLE)
+
registerAllNotificationChannels(context)
}
private fun registerAllNotificationChannels(context: Context) {
with(context) {
+ createChannel(providerChannel, getString(R.string.provider_notification_chanel))
createChannel(statisticsChannel, getString(R.string.statisctics_notification_chanel))
createChannel(connLostChannel, getString(R.string.connection_lost_notification_chanel))
createChannel(paymentStatusChannel, getString(R.string.payment_notification_chanel))
@@ -72,6 +81,19 @@ class AppNotificationManager(private val notificationManager: NotificationManage
}
}
+ fun createProviderNotification(): NotificationFactory {
+ return {
+ NotificationCompat.Builder(it, providerChannel)
+ .setSmallIcon(R.drawable.notification_logo)
+ .setContentTitle("Provider is active")
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setVibrate(LongArray(0))
+ .setContentIntent(pendingProviderIntent)
+ .setOnlyAlertOnce(true)
+ .build()
+ }
+ }
+
fun showStatisticsNotification(title: String, content: String) {
val disconnectIntent = Intent(context, AppBroadcastReceiver::class.java).apply {
action = ACTION_DISCONNECT
diff --git a/android/app/src/main/java/updated/mysterium/vpn/ui/base/BaseActivity.kt b/android/app/src/main/java/updated/mysterium/vpn/ui/base/BaseActivity.kt
index 8ed47d6e7..cde19dce7 100644
--- a/android/app/src/main/java/updated/mysterium/vpn/ui/base/BaseActivity.kt
+++ b/android/app/src/main/java/updated/mysterium/vpn/ui/base/BaseActivity.kt
@@ -35,6 +35,9 @@ import updated.mysterium.vpn.ui.connection.ConnectionActivity
import updated.mysterium.vpn.ui.custom.view.ConnectionToolbar
import updated.mysterium.vpn.ui.home.selection.HomeSelectionActivity
import updated.mysterium.vpn.ui.home.selection.HomeSelectionViewModel
+import updated.mysterium.vpn.ui.menu.MenuActivity
+import updated.mysterium.vpn.ui.provider.ProviderActivity
+
import java.util.*
abstract class BaseActivity : AppCompatActivity() {
@@ -266,7 +269,7 @@ abstract class BaseActivity : AppCompatActivity() {
) {
Intent(this, ConnectionActivity::class.java)
} else {
- Intent(this, HomeSelectionActivity::class.java)
+ Intent(this, MenuActivity::class.java)
}
intent.apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
diff --git a/android/app/src/main/java/updated/mysterium/vpn/ui/menu/MenuActivity.kt b/android/app/src/main/java/updated/mysterium/vpn/ui/menu/MenuActivity.kt
index 948b046b9..f5bda068a 100644
--- a/android/app/src/main/java/updated/mysterium/vpn/ui/menu/MenuActivity.kt
+++ b/android/app/src/main/java/updated/mysterium/vpn/ui/menu/MenuActivity.kt
@@ -22,6 +22,7 @@ import updated.mysterium.vpn.ui.monitoring.MonitoringActivity
import updated.mysterium.vpn.ui.profile.ProfileActivity
import updated.mysterium.vpn.ui.report.issue.ReportIssueActivity
import updated.mysterium.vpn.ui.settings.SettingsActivity
+import updated.mysterium.vpn.ui.provider.ProviderActivity
import updated.mysterium.vpn.ui.terms.TermsOfUseActivity
import updated.mysterium.vpn.ui.wallet.WalletActivity
@@ -50,6 +51,10 @@ class MenuActivity : BaseActivity() {
iconResId = R.drawable.menu_icon_settings,
titleResId = R.string.menu_list_item_settings,
),
+ MenuItem(
+ iconResId = R.drawable.menu_icon_settings,
+ titleResId = R.string.menu_list_item_provider,
+ ),
MenuItem(
iconResId = R.drawable.menu_icon_referral_deactivated,
titleResId = R.string.menu_item_referral_title,
@@ -185,6 +190,9 @@ class MenuActivity : BaseActivity() {
startActivity(Intent(this, SettingsActivity::class.java))
}
5 -> menuItem.onItemClickListener = {
+ startActivity(Intent(this, ProviderActivity::class.java))
+ }
+ 6 -> menuItem.onItemClickListener = {
// TODO("Implement navigation to Referral")
}
}
diff --git a/android/app/src/main/java/updated/mysterium/vpn/ui/provider/ProviderActivity.kt b/android/app/src/main/java/updated/mysterium/vpn/ui/provider/ProviderActivity.kt
new file mode 100644
index 000000000..b73a4cded
--- /dev/null
+++ b/android/app/src/main/java/updated/mysterium/vpn/ui/provider/ProviderActivity.kt
@@ -0,0 +1,90 @@
+package updated.mysterium.vpn.ui.provider
+
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.view.View
+import network.mysterium.vpn.R
+import network.mysterium.vpn.databinding.ActivityProviderBinding
+import org.koin.android.ext.android.inject
+import updated.mysterium.vpn.App
+import updated.mysterium.vpn.notification.AppNotificationManager
+import updated.mysterium.vpn.ui.base.BaseActivity
+
+
+class ProviderActivity : BaseActivity() {
+
+ private companion object {
+ const val TAG = "ProviderActivity"
+ }
+
+ private lateinit var binding: ActivityProviderBinding
+ private val viewModel: ProviderViewModel by inject()
+ private val notificationManager: AppNotificationManager by inject()
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityProviderBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ viewModel.init(
+ deferredMysteriumCoreService = App.getInstance(this).deferredMysteriumCoreService,
+ notificationManager = notificationManager
+ )
+ configure()
+ bindsAction()
+ }
+
+ override fun showConnectionHint() {
+ binding.connectionHint.visibility = View.VISIBLE
+ baseViewModel.hintShown()
+ }
+
+ private fun configure() {
+ initToolbar(binding.manualConnectToolbar)
+
+ viewModel.providerUpdate.observe(this) {
+ // prevent triggering of switch handler
+ binding.providerModeSwitch.tag = true
+ binding.providerModeSwitch.isChecked = it.active
+ binding.providerModeSwitch.tag = null
+ }
+
+ viewModel.providerServiceStatus.observe(this) {
+ fun getStatusTxt(a: Boolean): Int {
+ if (a) {
+ return R.string.service_active_title;
+ }
+ return R.string.service_idle_title
+ }
+ binding.titleSvcState1.setText(getStatusTxt(it.active[0]))
+ binding.titleSvcState2.setText(getStatusTxt(it.active[1]))
+ binding.titleSvcState3.setText(getStatusTxt(it.active[2]))
+ }
+ }
+
+ private fun bindsAction() {
+ binding.manualConnectToolbar.onConnectClickListener {
+ navigateToConnectionIfConnectedOrHome()
+ }
+ binding.manualConnectToolbar.onLeftButtonClicked {
+ finish()
+ }
+ binding.providerModeSwitch.setOnCheckedChangeListener { _, isChecked ->
+ if (binding.providerModeSwitch.tag == null) {
+ viewModel.toggleProvider(isChecked)
+ }
+ }
+ binding.buttonUI.setOnClickListener {
+ try {
+ val myIntent = Intent(Intent.ACTION_VIEW, Uri.parse("http://localhost:4449"))
+ startActivity(myIntent)
+ } catch (e: ActivityNotFoundException) {
+ e.printStackTrace()
+ }
+ }
+ }
+
+}
diff --git a/android/app/src/main/java/updated/mysterium/vpn/ui/provider/ProviderViewModel.kt b/android/app/src/main/java/updated/mysterium/vpn/ui/provider/ProviderViewModel.kt
new file mode 100644
index 000000000..247475b1b
--- /dev/null
+++ b/android/app/src/main/java/updated/mysterium/vpn/ui/provider/ProviderViewModel.kt
@@ -0,0 +1,122 @@
+package updated.mysterium.vpn.ui.provider
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.*
+import updated.mysterium.vpn.core.DeferredNode
+import updated.mysterium.vpn.core.MysteriumCoreService
+import updated.mysterium.vpn.model.manual.connect.ProviderState
+import updated.mysterium.vpn.model.notification.NotificationChannels
+import updated.mysterium.vpn.network.provider.usecase.UseCaseProvider
+import updated.mysterium.vpn.notification.AppNotificationManager
+
+data class ServiceState(
+ var active: Array,
+)
+
+class ProviderViewModel(useCaseProvider: UseCaseProvider) : ViewModel() {
+
+ private companion object {
+ const val TAG = "ProviderViewModel"
+ }
+
+
+ val providerUpdate: LiveData
+ get() = _providerUpdate
+ private val _providerUpdate = MutableLiveData()
+
+
+ private val servicesState = ServiceState( Array(3) {false} )
+ val providerServiceStatus: LiveData
+ get() = _providerServiceStatus
+ private val _providerServiceStatus = MutableLiveData()
+
+
+ private var coreService: MysteriumCoreService? = null
+ private val connectionUseCase = useCaseProvider.connection()
+ private lateinit var appNotificationManager: AppNotificationManager
+ private var deferredNode = DeferredNode()
+
+ val handler = CoroutineExceptionHandler { _, exception ->
+ Log.i(TAG, exception.localizedMessage ?: exception.toString())
+ }
+
+ fun init(
+ deferredMysteriumCoreService: CompletableDeferred,
+ notificationManager: AppNotificationManager,
+ ) {
+ viewModelScope.launch(handler) {
+ appNotificationManager = notificationManager
+ coreService = deferredMysteriumCoreService.await()
+
+ // Restart a node in case of app crash (on activity restore), thus regaining control of the node
+ startDeferredNode()
+
+ val initialState = ProviderState(
+ active = getIsProviderActive(),
+ )
+ _providerUpdate.postValue(initialState)
+
+ connectionUseCase.serviceStatusChangeCallback {
+ val running = (it.status == "Running")
+ when (it.service) {
+ "wireguard" -> servicesState.active[0] = running
+ "data_transfer" -> servicesState.active[1] = running
+ "scraping" -> servicesState.active[2] = running
+ }
+ _providerServiceStatus.postValue(servicesState)
+
+ // make provider switch state "false" if all services are disabled
+ var someEnabled = false
+ for (x in servicesState.active) {
+ if (x) {
+ someEnabled = true
+ break
+ }
+ }
+ _providerUpdate.postValue(ProviderState(someEnabled))
+ }
+ }
+ }
+
+ fun toggleProvider(isChecked: Boolean) {
+ CoroutineScope(Dispatchers.IO).launch {
+ coreService?.let {
+ it.startProvider(isChecked)
+ if (isChecked) {
+ it.startForegroundWithNotification(
+ NotificationChannels.PROVIDER_NOTIFICATION,
+ appNotificationManager.createProviderNotification()
+ )
+ } else {
+ it.stopForeground()
+ }
+ }
+ }
+ }
+
+ private suspend fun startDeferredNode() {
+ if (deferredNode.startedOrStarting()) {
+ coreService?.getDeferredNode()?.let {
+ deferredNode = it
+ }
+ } else {
+ coreService?.let {
+ deferredNode.start(it)
+ }
+ }
+
+ connectionUseCase.initDeferredNode(deferredNode)
+ connectionUseCase.getIdentity()
+ }
+
+ private fun getIsProviderActive(): Boolean {
+ coreService?.let {
+ return it.isProviderActive()
+ }
+ return false
+ }
+}
diff --git a/android/app/src/main/res/layout/activity_profile.xml b/android/app/src/main/res/layout/activity_profile.xml
index 8f8f38e02..115a11989 100644
--- a/android/app/src/main/res/layout/activity_profile.xml
+++ b/android/app/src/main/res/layout/activity_profile.xml
@@ -47,8 +47,7 @@
+ android:layout_height="wrap_content">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index ba342c9f5..4b22f8ca1 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -1,5 +1,5 @@
- MysteriumVPN
+ MysteriumVPN Provider
Close
@@ -86,6 +86,8 @@
Earn VPN Credits
Connection history
Settings
+ Provider mode
+
Report an issue
Get help
Terms of Use
@@ -193,10 +195,21 @@
Select Country or Region
Select State
+
+ Server
+ Myst provider
+ Enable
+
Settings
Identity is your Mysterium internal user ID. Never send ether or any kind of ERC20 tokens there.
DNS
+ wireguard
+ data transfer
+ scraping
+ idle
+ active
+
Resident country
System
Provider
@@ -318,4 +331,6 @@
Lost Connection
Payments
Reminder Notifications
+ Provider mode
+
diff --git a/fastlane/README.md b/fastlane/README.md
index d991d32b5..992afb903 100644
--- a/fastlane/README.md
+++ b/fastlane/README.md
@@ -1,49 +1,64 @@
fastlane documentation
-================
+----
+
# Installation
Make sure you have the latest version of the Xcode command line tools installed:
-```
+```sh
xcode-select --install
```
-Install _fastlane_ using
-```
-[sudo] gem install fastlane -NV
-```
-or alternatively using `brew install fastlane`
+For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
# Available Actions
+
## Android
+
### android buildDebug
+
+```sh
+[bundle exec] fastlane android buildDebug
```
-fastlane android buildDebug
-```
+
Builds the debug code
+
### android buildRelease
+
+```sh
+[bundle exec] fastlane android buildRelease
```
-fastlane android buildRelease
-```
+
Builds the release code
+
### android test
+
+```sh
+[bundle exec] fastlane android test
```
-fastlane android test
-```
+
Runs all the tests
+
### android build
+
+```sh
+[bundle exec] fastlane android build
```
-fastlane android build
-```
+
Build release build locally
+
### android internal
+
+```sh
+[bundle exec] fastlane android internal
```
-fastlane android internal
-```
+
Push a new internal build to Play Store
----
-This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
-More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
-The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
+This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
+
+More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
+
+The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).