Добавлен Yandex

This commit is contained in:
2026-04-19 00:22:05 +03:00
parent 586e2b61fd
commit b3c00b1719
24 changed files with 710 additions and 49 deletions

View File

@@ -67,6 +67,8 @@ dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)

View File

@@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

View File

@@ -9,30 +9,37 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.github.nullptroma.wallenc.app.auth.YandexSignInService
import com.github.nullptroma.wallenc.presentation.WallencUi
import dagger.hilt.android.AndroidEntryPoint
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
lateinit var yandexSignInService: YandexSignInService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
yandexSignInService.registerWith(this)
enableEdgeToEdge()
requestNotificationPermissionIfNeeded()
Timber.plant(Timber.DebugTree())
// val sdk = YandexAuthSdk.create(YandexAuthOptions(applicationContext, true))
// val launcher =
// registerForActivityResult(sdk.contract) { result -> handleResult(result) }
// val loginOptions = YandexAuthLoginOptions(LoginType.CHROME_TAB)
setContent {
WallencUi()
}
}
override fun onDestroy() {
yandexSignInService.unregister(this)
super.onDestroy()
}
private fun requestNotificationPermissionIfNeeded() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
val granted = ContextCompat.checkSelfPermission(
@@ -50,12 +57,4 @@ class MainActivity : ComponentActivity() {
companion object {
private const val NOTIFICATION_PERMISSION_REQUEST_CODE = 100
}
// private fun handleResult(result: YandexAuthResult) {
// when (result) {
// is YandexAuthResult.Success -> Toast.makeText(applicationContext, "Success: ${result.token}", Toast.LENGTH_SHORT).show()
// is YandexAuthResult.Failure -> Toast.makeText(applicationContext, "Success: ${result.exception}", Toast.LENGTH_SHORT).show()
// YandexAuthResult.Cancelled -> Toast.makeText(applicationContext, "Cancel", Toast.LENGTH_SHORT).show()
// }
// }
}

View File

@@ -0,0 +1,83 @@
package com.github.nullptroma.wallenc.app.auth
import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import com.github.nullptroma.wallenc.domain.auth.RemoteYandexAuthResult
import com.github.nullptroma.wallenc.domain.auth.RemoteYandexSignInLauncher
import com.yandex.authsdk.YandexAuthLoginOptions
import com.yandex.authsdk.YandexAuthOptions
import com.yandex.authsdk.YandexAuthResult
import com.yandex.authsdk.YandexAuthSdk
import com.yandex.authsdk.internal.strategy.LoginType
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton
/**
* Императивный вход через Yandex Auth SDK: [registerWith] в [ComponentActivity.onCreate],
* [unregister] в [ComponentActivity.onDestroy], чтобы синглтон не держал [ActivityResultLauncher]
* дольше жизни Activity.
*
* При смене конфигурации новая Activity может вызвать [registerWith] до [unregister] старой —
* тогда владелец и launcher обновляются; [unregister] для уже неактуального экземпляра — no-op.
*/
@Singleton
class YandexSignInService @Inject constructor(
@ApplicationContext appContext: Context,
) : RemoteYandexSignInLauncher {
private val sdk = YandexAuthSdk.create(YandexAuthOptions(appContext, true))
private var launcher: ActivityResultLauncher<YandexAuthLoginOptions>? = null
private var registrationOwner: ComponentActivity? = null
@Volatile
private var pending: ((RemoteYandexAuthResult) -> Unit)? = null
fun registerWith(activity: ComponentActivity) {
if (registrationOwner === activity && launcher != null) return
launcher = activity.registerForActivityResult(sdk.contract) { result ->
val mapped = mapYandexResult(result)
val cb = synchronized(this) {
val p = pending
pending = null
p
}
cb?.invoke(mapped)
}
registrationOwner = activity
}
fun unregister(activity: ComponentActivity) {
if (registrationOwner !== activity) return
launcher = null
registrationOwner = null
val cb = synchronized(this) {
val p = pending
pending = null
p
}
cb?.invoke(RemoteYandexAuthResult.Cancelled)
}
override fun launch(onResult: (RemoteYandexAuthResult) -> Unit) {
val l = launcher
?: error("YandexSignInService: call registerWith(activity) from MainActivity.onCreate first")
synchronized(this) {
pending = onResult
}
l.launch(YandexAuthLoginOptions(LoginType.WEBVIEW))
}
private fun mapYandexResult(result: YandexAuthResult): RemoteYandexAuthResult = when (result) {
is YandexAuthResult.Success ->
RemoteYandexAuthResult.Success(result.token.value)
is YandexAuthResult.Failure ->
RemoteYandexAuthResult.Failure(
result.exception.message ?: result.exception.toString(),
)
YandexAuthResult.Cancelled -> RemoteYandexAuthResult.Cancelled
}
}

View File

@@ -0,0 +1,20 @@
package com.github.nullptroma.wallenc.app.di.modules.auth
import com.github.nullptroma.wallenc.app.auth.YandexSignInService
import com.github.nullptroma.wallenc.domain.auth.RemoteYandexSignInLauncher
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
abstract class YandexAuthModule {
@Binds
@Singleton
abstract fun bindRemoteYandexSignInLauncher(
impl: YandexSignInService,
): RemoteYandexSignInLauncher
}

View File

@@ -5,6 +5,7 @@ import com.github.nullptroma.wallenc.data.db.RoomFactory
import com.github.nullptroma.wallenc.data.db.app.IAppDb
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao
import com.github.nullptroma.wallenc.data.db.app.dao.StorageMetaInfoDao
import com.github.nullptroma.wallenc.data.db.app.dao.YandexAccountDao
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -32,6 +33,12 @@ class RoomModule {
return database.storageMetaInfoDao
}
@Provides
@Singleton
fun provideYandexAccountDao(database: IAppDb): YandexAccountDao {
return database.yandexAccountDao
}
@Provides
@Singleton
fun provideAppDb(

View File

@@ -4,6 +4,9 @@ import android.content.Context
import com.github.nullptroma.wallenc.app.di.modules.app.IoDispatcher
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao
import com.github.nullptroma.wallenc.data.db.app.dao.StorageMetaInfoDao
import com.github.nullptroma.wallenc.data.db.app.dao.YandexAccountDao
import com.github.nullptroma.wallenc.data.network.YandexUserInfoApi
import com.github.nullptroma.wallenc.data.network.YandexUserInfoApiFactory
import com.github.nullptroma.wallenc.data.db.app.repository.StorageKeyMapRepository
import com.github.nullptroma.wallenc.data.db.app.repository.StorageMetaInfoRepository
import com.github.nullptroma.wallenc.data.tasks.TaskOrchestrator
@@ -28,14 +31,20 @@ class SingletonModule {
@IoDispatcher ioDispatcher: CoroutineDispatcher,
): ITaskOrchestrator = TaskOrchestrator(ioDispatcher)
@Provides
@Singleton
fun provideYandexUserInfoApi(): YandexUserInfoApi = YandexUserInfoApiFactory.create()
@Provides
@Singleton
fun provideVaultsManager(
@IoDispatcher ioDispatcher: CoroutineDispatcher,
@ApplicationContext context: Context,
keyRepo: StorageKeyMapRepository,
yandexAccountDao: YandexAccountDao,
yandexUserInfoApi: YandexUserInfoApi,
): IVaultsManager {
return VaultsManager(ioDispatcher, context, keyRepo)
return VaultsManager(ioDispatcher, context, keyRepo, yandexAccountDao, yandexUserInfoApi)
}
@Provides