From a8fd1cebc6a9b6f2889dab7c682bd714884d9a28 Mon Sep 17 00:00:00 2001 From: xiaozhikang Date: Sun, 9 Jul 2023 15:12:26 +0800 Subject: [PATCH] accept proxy address from intent --- app/src/main/AndroidManifest.xml | 3 +- .../create/proxytoggle/main/MainActivity.kt | 12 ++++ .../viewmodel/ProxyManagerViewModel.kt | 41 ++++++++++++- .../viewmodel/ProxyManagerViewModelTest.kt | 60 ++++++++++++++++++- 4 files changed, 113 insertions(+), 3 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index add5a54..9940ef9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,7 +22,8 @@ + android:windowSoftInputMode="adjustResize" + android:launchMode="singleInstance"> diff --git a/app/src/main/java/com/kinandcarta/create/proxytoggle/main/MainActivity.kt b/app/src/main/java/com/kinandcarta/create/proxytoggle/main/MainActivity.kt index 0c4f53e..41f738e 100644 --- a/app/src/main/java/com/kinandcarta/create/proxytoggle/main/MainActivity.kt +++ b/app/src/main/java/com/kinandcarta/create/proxytoggle/main/MainActivity.kt @@ -1,8 +1,10 @@ package com.kinandcarta.create.proxytoggle.main import android.Manifest +import android.content.Intent import android.content.pm.PackageManager import android.os.Bundle +import android.util.Log import androidx.activity.compose.setContent import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity @@ -17,12 +19,14 @@ import androidx.core.content.ContextCompat import com.kinandcarta.create.proxytoggle.core.ui.theme.ProxyToggleTheme import com.kinandcarta.create.proxytoggle.manager.view.screen.BlockAppScreen import com.kinandcarta.create.proxytoggle.manager.view.screen.ProxyManagerScreen +import com.kinandcarta.create.proxytoggle.manager.viewmodel.ProxyManagerViewModel import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class MainActivity : AppCompatActivity() { private val viewModel: MainViewModel by viewModels() + private val proxyViewModel: ProxyManagerViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -36,6 +40,14 @@ class MainActivity : AppCompatActivity() { MainScreen(useDarkTheme = useDarkTheme, useVerticalLayout = useVerticalLayout) } } + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + intent?.getStringExtra(ProxyManagerViewModel.PROXY_KEY)?.takeIf { it.isNotBlank() }?.let { + Log.i("Intent", "On new intent $it") + proxyViewModel.onSetProxy(it) + } + } } @Composable diff --git a/feature/manager/src/main/java/com/kinandcarta/create/proxytoggle/manager/viewmodel/ProxyManagerViewModel.kt b/feature/manager/src/main/java/com/kinandcarta/create/proxytoggle/manager/viewmodel/ProxyManagerViewModel.kt index 34e3fed..b294a83 100644 --- a/feature/manager/src/main/java/com/kinandcarta/create/proxytoggle/manager/viewmodel/ProxyManagerViewModel.kt +++ b/feature/manager/src/main/java/com/kinandcarta/create/proxytoggle/manager/viewmodel/ProxyManagerViewModel.kt @@ -1,10 +1,12 @@ package com.kinandcarta.create.proxytoggle.manager.viewmodel +import android.util.Log import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting import androidx.compose.runtime.MutableState import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.kinandcarta.create.proxytoggle.core.common.proxy.Proxy @@ -16,7 +18,9 @@ import com.kinandcarta.create.proxytoggle.repository.userprefs.UserPreferencesRe import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch +import kotlinx.coroutines.yield import javax.inject.Inject @Suppress("TooManyFunctions") @@ -25,7 +29,8 @@ class ProxyManagerViewModel @Inject constructor( private val deviceSettingsManager: DeviceSettingsManager, private val proxyValidator: ProxyValidator, private val appDataRepository: AppDataRepository, - private val userPreferencesRepository: UserPreferencesRepository + private val userPreferencesRepository: UserPreferencesRepository, + private val savedStateHandle: SavedStateHandle, ) : ViewModel() { private var _uiState = mutableStateOf( @@ -63,6 +68,13 @@ class ProxyManagerViewModel @Inject constructor( } } } + + viewModelScope.launch { + savedStateHandle.getStateFlow(PROXY_KEY, "").filter { it.isNotBlank() }.collect { + Log.i("Intent", "On start intent $it") + onSetProxy(it) + } + } } fun onUserInteraction(userInteraction: UserInteraction) { @@ -75,6 +87,31 @@ class ProxyManagerViewModel @Inject constructor( } } + fun onSetProxy(proxyStr: String) { + viewModelScope.launch { + if (deviceSettingsManager.proxySetting.value.isEnabled) { + // Turn off proxy before setting the proxy ip and port + deviceSettingsManager.disableProxy() + + while (uiState.value is UiState.Connected) { + yield() + } + } + val inputArray = proxyStr.split(':') + val inputIp = inputArray.firstOrNull() + val inputPort = inputArray.getOrNull(1) + inputIp?.takeIf { it.isNotBlank() }?.let { + onAddressChanged(it) + } + + inputPort?.takeIf { it.isNotBlank() }?.let { + onPortChanged(it) + } + + // Leave it for user to turn on the proxy + } + } + fun onForceFocusExecuted() { updateDisconnectedState { it.copy( @@ -236,5 +273,7 @@ class ProxyManagerViewModel @Inject constructor( companion object { // NOTE: necessary delay to refocus & announce existing error on next attempt to connect! private const val ERROR_DELAY = 50L + + const val PROXY_KEY = "proxy" } } diff --git a/feature/manager/src/test/java/com/kinandcarta/create/proxytoggle/manager/viewmodel/ProxyManagerViewModelTest.kt b/feature/manager/src/test/java/com/kinandcarta/create/proxytoggle/manager/viewmodel/ProxyManagerViewModelTest.kt index 75cdd5b..008d73d 100644 --- a/feature/manager/src/test/java/com/kinandcarta/create/proxytoggle/manager/viewmodel/ProxyManagerViewModelTest.kt +++ b/feature/manager/src/test/java/com/kinandcarta/create/proxytoggle/manager/viewmodel/ProxyManagerViewModelTest.kt @@ -1,10 +1,12 @@ package com.kinandcarta.create.proxytoggle.manager.viewmodel +import androidx.lifecycle.SavedStateHandle import com.google.common.truth.Truth.assertThat import com.kinandcarta.create.proxytoggle.core.common.proxy.Proxy import com.kinandcarta.create.proxytoggle.core.common.proxy.ProxyValidator import com.kinandcarta.create.proxytoggle.core.common.stub.Stubs.PROXY_ADDRESS import com.kinandcarta.create.proxytoggle.core.common.stub.Stubs.PROXY_PORT +import com.kinandcarta.create.proxytoggle.core.common.stub.Stubs.VALID_PROXY import com.kinandcarta.create.proxytoggle.manager.R import com.kinandcarta.create.proxytoggle.repository.appdata.AppDataRepository import com.kinandcarta.create.proxytoggle.repository.devicesettings.DeviceSettingsManager @@ -50,6 +52,8 @@ class ProxyManagerViewModelTest { private val fakeProxyStateFlow = MutableStateFlow(Proxy.Disabled) + private val savedStateHandle = SavedStateHandle() + private lateinit var subject: ProxyManagerViewModel @Before @@ -304,11 +308,65 @@ class ProxyManagerViewModelTest { } } + @Test + fun `onSetProxy() - Auto fill in address and port`() { + // WHEN + dispatcher.scheduler.advanceUntilIdle() + every { mockProxyValidator.isValidIP(any()) } returns true + every { mockProxyValidator.isValidPort(any()) } returns true + subject.onSetProxy(VALID_PROXY) + dispatcher.scheduler.advanceUntilIdle() + + // THEN + assertThat(subject.uiState.value).isEqualTo( + ProxyManagerViewModel.UiState.Disconnected( + addressState = ProxyManagerViewModel.TextFieldState(text = PROXY_ADDRESS), + portState = ProxyManagerViewModel.TextFieldState(text = PROXY_PORT), + pastProxies = emptyList() + ) + ) + } + + @Test + fun `onSetProxy() - Auto close previous proxy before filling in address and port`() { + // GIVEN + every { mockProxyValidator.isValidIP(any()) } returns true + every { mockProxyValidator.isValidPort(any()) } returns true + givenDisconnectedWith(address = "2.3.4.5", port = "333") + subject.onUserInteraction(ProxyManagerViewModel.UserInteraction.ToggleProxyClicked) + dispatcher.scheduler.advanceUntilIdle() + + // Check now is connected + assertThat(subject.uiState.value).isInstanceOf(ProxyManagerViewModel.UiState.Connected::class.java) + coVerify { + mockProxyValidator.isValidIP("2.3.4.5") + mockProxyValidator.isValidPort("333") + mockDeviceSettingsManager.enableProxy(Proxy("2.3.4.5", "333")) + } + + // WHEN + subject.onSetProxy(VALID_PROXY) + dispatcher.scheduler.advanceUntilIdle() + + // THEN + assertThat(subject.uiState.value).isEqualTo( + ProxyManagerViewModel.UiState.Disconnected( + addressState = ProxyManagerViewModel.TextFieldState(text = PROXY_ADDRESS), + portState = ProxyManagerViewModel.TextFieldState(text = PROXY_PORT), + pastProxies = emptyList() + ) + ) + verify { + mockDeviceSettingsManager.disableProxy() + } + } + private fun initSubject() = ProxyManagerViewModel( mockDeviceSettingsManager, mockProxyValidator, mockAppDataRepository, - mockUserPreferencesRepository + mockUserPreferencesRepository, + savedStateHandle ) private fun givenPastProxies(pastProxies: List) {