Чтобы использовать свой UI, вызывайте SDK напрямую. Создайте PaspoID из host-Activity и вызовите authenticate из корутины. Метод не бросает исключений; любой исход, включая отмену и отсутствие приложения Paspo ID, возвращается как PaspoAuthResult.
Действуют два ограничения:
authenticate; новый вызов сбрасывает предыдущий.PaspoID держит ссылку на Activity и должен оставаться в UI-слое. Не храните его в ViewModel или синглтоне; передавайте во ViewModel только результат.@Composable
fun LoginScreen() {
val activity = LocalActivity.current as? ComponentActivity ?: return
val paspoId = remember(activity) { PaspoID(activity) }
val scope = rememberCoroutineScope()
Button(
onClick = {
scope.launch {
onPaspoResult(paspoId.authenticate(PaspoScope.PHONES, backend.requestSsoNonce()))
}
},
) {
Text("Войти через Paspo")
}
}
remember(activity) хранит единственный PaspoID между рекомпозициями; rememberCoroutineScope отменяет вызов, если пользователь уходит с экрана.
class LoginActivity : AppCompatActivity() {
private val paspoId = PaspoID(this)
private fun signInWithPaspo() = lifecycleScope.launch {
onPaspoResult(paspoId.authenticate(PaspoScope.PHONES, backend.requestSsoNonce()))
}
}
class LoginFragment : Fragment(R.layout.fragment_login) {
// lazy: на момент создания фрагмента Activity ещё не присоединена
private val paspoId by lazy { PaspoID(requireActivity() as ComponentActivity) }
private fun signInWithPaspo() = viewLifecycleOwner.lifecycleScope.launch {
onPaspoResult(paspoId.authenticate(PaspoScope.PHONES, backend.requestSsoNonce()))
}
}
Запускайте на viewLifecycleOwner.lifecycleScope, а не на скоупе самого фрагмента, чтобы колбэк не отработал на уничтоженной view.
Если Activity пересоздаётся, пока открыт экран Paspo ID (поворот, смерть процесса), результат теряется. Обработайте это как отмену и дайте пользователю повторить; проще всего зафиксировать ориентацию экрана входа.
Пользователь может начать вход и изнутри приложения Paspo ID. В этом случае Paspo ID запускает ваше приложение с action paspo.id.ssoprovider.action.AUTHENTICATE (константа PaspoID.ACTION_SIGN_IN). Поддержка этой точки входа опциональна.
Объявите intent-filter на Activity входа:
<activity
android:name=".LoginActivity"
android:exported="true">
<intent-filter>
<action android:name="paspo.id.ssoprovider.action.AUTHENTICATE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
и запускайте поток при получении action:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (intent.action == PaspoID.ACTION_SIGN_IN) {
signInWithPaspo()
}
}
Intent не несёт дополнительных данных; это лишь сигнал о намерении пользователя войти.
Чтобы адаптировать UI до любого взаимодействия:
if (paspoId.checkInstallation()) {
// Paspo ID установлен
}
Вызов опционален: authenticate выполняет проверку сам и, если Paspo ID не установлен, открывает Play Store и возвращает NotInstalled.
paspoId.cleanup() очищает эфемерные ключи. SDK вызывает его автоматически при завершении и при ошибках; вручную он нужен только если вы сами прерываете поток, например когда пользователь уходит с экрана входа.