Repo Created
This commit is contained in:
parent
eb305e2886
commit
a8c22c65db
4784 changed files with 329907 additions and 2 deletions
45
play-services-auth-api-phone/build.gradle
Normal file
45
play-services-auth-api-phone/build.gradle
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'signing'
|
||||
|
||||
android {
|
||||
namespace "com.google.android.gms.auth.api.phone"
|
||||
|
||||
compileSdkVersion androidCompileSdk
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
buildFeatures {
|
||||
aidl = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionName version
|
||||
minSdkVersion androidMinSdk
|
||||
targetSdkVersion androidTargetSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
}
|
||||
}
|
||||
|
||||
apply from: '../gradle/publish-android.gradle'
|
||||
|
||||
description = 'microG implementation of play-services-auth-api-phone'
|
||||
|
||||
dependencies {
|
||||
// Dependencies from play-services-auth-api-phone:18.2.0
|
||||
api project(':play-services-base')
|
||||
api project(':play-services-basement')
|
||||
api project(':play-services-tasks')
|
||||
api 'org.jetbrains.kotlin:kotlin-stdlib:1.8.21'
|
||||
api 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21'
|
||||
api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3'
|
||||
// api 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.0'
|
||||
}
|
||||
44
play-services-auth-api-phone/core/build.gradle
Normal file
44
play-services-auth-api-phone/core/build.gradle
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
dependencies {
|
||||
api project(':play-services-auth-api-phone')
|
||||
implementation project(':play-services-base-core')
|
||||
|
||||
implementation "androidx.appcompat:appcompat:$appcompatVersion"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "org.microg.gms.auth.phone"
|
||||
|
||||
compileSdkVersion androidCompileSdk
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
defaultConfig {
|
||||
versionName version
|
||||
minSdkVersion androidMinSdk
|
||||
targetSdkVersion androidTargetSdk
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = 1.8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'MissingTranslation', 'GetLocales'
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
||||
|
||||
<application>
|
||||
|
||||
<activity
|
||||
android:name="org.microg.gms.auth.phone.UserConsentPromptActivity"
|
||||
android:exported="true"
|
||||
android:process=":ui"
|
||||
android:theme="@style/Theme.AppCompat.DayNight.Dialog.Alert.NoActionBar" />
|
||||
|
||||
<activity
|
||||
android:name="org.microg.gms.auth.phone.AskPermissionActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="false"
|
||||
android:process=":ui"
|
||||
android:theme="@style/Theme.Translucent" />
|
||||
|
||||
<service
|
||||
android:name="org.microg.gms.auth.phone.SmsRetrieverService"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.gms.auth.api.phone.service.SmsRetrieverApiService.START" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.phone
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Bundle
|
||||
import android.os.Message
|
||||
import android.os.Messenger
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.os.bundleOf
|
||||
|
||||
private const val TAG = "AskPermission"
|
||||
private const val REQUEST_CODE_PERMISSION = 101
|
||||
private val ALLOWED_PERMISSIONS = setOf(Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_CONTACTS)
|
||||
|
||||
class AskPermissionActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val permissions = intent.getStringArrayExtra(EXTRA_PERMISSIONS) ?: arrayOf(Manifest.permission.RECEIVE_SMS)
|
||||
Log.d(TAG, "Requesting permissions: ${permissions.toList()}")
|
||||
if (SDK_INT < 23 || permissions.any { it !in ALLOWED_PERMISSIONS }) {
|
||||
sendReply(RESULT_CANCELED)
|
||||
finish()
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_PERMISSION)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendReply(code: Int = RESULT_OK, extras: Bundle = Bundle.EMPTY) {
|
||||
intent.getParcelableExtra<Messenger>(EXTRA_MESSENGER)?.let {
|
||||
it.send(Message.obtain().apply {
|
||||
what = code
|
||||
data = extras
|
||||
})
|
||||
}
|
||||
setResult(code, Intent().apply { putExtras(extras) })
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
if (requestCode == REQUEST_CODE_PERMISSION) {
|
||||
sendReply(extras = bundleOf(EXTRA_GRANT_RESULTS to grantResults))
|
||||
finish()
|
||||
} else {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.phone
|
||||
|
||||
import android.Manifest.permission.READ_CONTACTS
|
||||
import android.Manifest.permission.RECEIVE_SMS
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||
import android.database.Cursor
|
||||
import android.os.*
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.ContactsContract.CommonDataKinds.Phone
|
||||
import android.provider.Telephony
|
||||
import android.telephony.SmsMessage
|
||||
import android.text.TextUtils
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import androidx.core.app.PendingIntentCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.google.android.gms.auth.api.phone.SmsRetriever
|
||||
import com.google.android.gms.common.api.CommonStatusCodes
|
||||
import com.google.android.gms.common.api.Status
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.microg.gms.auth.phone.SmsRetrieverRequestType.RETRIEVER
|
||||
import org.microg.gms.auth.phone.SmsRetrieverRequestType.USER_CONSENT
|
||||
import org.microg.gms.common.Constants
|
||||
import org.microg.gms.utils.getSignatures
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.MessageDigest
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
|
||||
private const val TAG = "SmsRetrieverCore"
|
||||
|
||||
private const val ACTION_SMS_RETRIEVE_TIMEOUT = "org.microg.gms.auth.phone.ACTION_SMS_RETRIEVE_TIMEOUT"
|
||||
private const val EXTRA_REQUEST_ID = "requestId"
|
||||
private const val TIMEOUT = 1000 * 60 * 5 // 5 minutes
|
||||
private const val MESSAGE_MAX_LEN = 140
|
||||
|
||||
class SmsRetrieverCore(private val context: Context, override val lifecycle: Lifecycle) : LifecycleOwner, DefaultLifecycleObserver {
|
||||
private val requests: HashMap<Int, SmsRetrieverRequest> = hashMapOf()
|
||||
private val requestIdCounter = AtomicInteger(0)
|
||||
private lateinit var timeoutBroadcastReceiver: BroadcastReceiver
|
||||
private lateinit var smsBroadcastReceiver: BroadcastReceiver
|
||||
private var requestCode = 0
|
||||
private val alarmManager: AlarmManager
|
||||
get() = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
|
||||
init {
|
||||
lifecycle.addObserver(this)
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
private fun configureBroadcastListenersIfNeeded() {
|
||||
synchronized(this) {
|
||||
if (!this::timeoutBroadcastReceiver.isInitialized) {
|
||||
val intentFilter = IntentFilter(ACTION_SMS_RETRIEVE_TIMEOUT)
|
||||
timeoutBroadcastReceiver = TimeoutReceiver()
|
||||
ContextCompat.registerReceiver(context, timeoutBroadcastReceiver, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
||||
}
|
||||
if (!this::smsBroadcastReceiver.isInitialized) {
|
||||
val intentFilter = IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)
|
||||
intentFilter.priority = 999
|
||||
smsBroadcastReceiver = SmsReceiver()
|
||||
context.registerReceiver(smsBroadcastReceiver, intentFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun ensureReady(permissions: Array<String>): Boolean {
|
||||
if (SDK_INT < 19) throw RuntimeException("Version not supported")
|
||||
if (!ensurePermission(permissions)) return false
|
||||
configureBroadcastListenersIfNeeded()
|
||||
return true
|
||||
}
|
||||
|
||||
suspend fun startSmsRetriever(packageName: String) {
|
||||
val appHashString = getHashString(packageName)
|
||||
|
||||
if (!ensureReady(arrayOf(RECEIVE_SMS)))
|
||||
throw RuntimeException("Initialization failed")
|
||||
if (anyOtherPackageHasHashString(packageName, appHashString))
|
||||
throw RuntimeException("Collision in hash string, can't use SMS Retriever API")
|
||||
if (requests.values.any { it.packageName == packageName && it.appHashString == appHashString && it.type == RETRIEVER })
|
||||
throw RuntimeException("App already listening")
|
||||
|
||||
val request = SmsRetrieverRequest(
|
||||
id = requestIdCounter.incrementAndGet(),
|
||||
type = RETRIEVER,
|
||||
packageName = packageName,
|
||||
appHashString = appHashString,
|
||||
timeoutPendingIntent = getTimeoutPendingIntent(context, packageName)
|
||||
)
|
||||
requests[request.id] = request
|
||||
alarmManager.set(AlarmManager.RTC, request.creation + TIMEOUT, request.timeoutPendingIntent)
|
||||
}
|
||||
|
||||
suspend fun startWithConsentPrompt(packageName: String, senderPhoneNumber: String?) {
|
||||
if (!ensureReady(arrayOf(RECEIVE_SMS, READ_CONTACTS)))
|
||||
throw RuntimeException("Initialization failed")
|
||||
if (requests.values.any { it.packageName == packageName && it.senderPhoneNumber == senderPhoneNumber && it.type == USER_CONSENT })
|
||||
throw RuntimeException("App already listening")
|
||||
|
||||
val request = SmsRetrieverRequest(
|
||||
id = requestIdCounter.incrementAndGet(),
|
||||
type = USER_CONSENT,
|
||||
packageName = packageName,
|
||||
senderPhoneNumber = senderPhoneNumber,
|
||||
timeoutPendingIntent = getTimeoutPendingIntent(context, packageName)
|
||||
)
|
||||
requests[request.id] = request
|
||||
alarmManager.set(AlarmManager.RTC, request.creation + TIMEOUT, request.timeoutPendingIntent)
|
||||
}
|
||||
|
||||
fun hasOngoingUserConsentRequest(): Boolean {
|
||||
return requests.values.any { it.type == USER_CONSENT }
|
||||
}
|
||||
|
||||
private fun sendRetrieverBroadcast(request: SmsRetrieverRequest, messageBody: String) {
|
||||
sendReply(request, Status.SUCCESS, bundleOf(SmsRetriever.EXTRA_SMS_MESSAGE to messageBody))
|
||||
}
|
||||
|
||||
private fun sendUserConsentBroadcast(request: SmsRetrieverRequest, messageBody: String) {
|
||||
val userConsentIntent = Intent(context, UserConsentPromptActivity::class.java)
|
||||
userConsentIntent.setPackage(Constants.GMS_PACKAGE_NAME)
|
||||
userConsentIntent.putExtra(EXTRA_MESSENGER, Messenger(object : Handler(Looper.getMainLooper()) {
|
||||
override fun handleMessage(msg: Message) {
|
||||
if (Binder.getCallingUid() == Process.myUid()) {
|
||||
if (msg.what == MSG_REQUEST_MESSAGE_BODY) {
|
||||
msg.replyTo?.send(Message.obtain().apply {
|
||||
what = 1
|
||||
data = bundleOf("message" to messageBody)
|
||||
})
|
||||
} else if (msg.what == MSG_CONSUME_MESSAGE) {
|
||||
finishRequest(request)
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
sendReply(request, Status.SUCCESS, bundleOf(SmsRetriever.EXTRA_CONSENT_INTENT to userConsentIntent), false)
|
||||
}
|
||||
|
||||
private fun getTimeoutPendingIntent(context: Context, packageName: String): PendingIntent {
|
||||
val intent = Intent(ACTION_SMS_RETRIEVE_TIMEOUT)
|
||||
intent.setPackage(packageName)
|
||||
return PendingIntentCompat.getBroadcast(context, ++requestCode, intent, 0, false)!!
|
||||
}
|
||||
|
||||
private fun tryHandleIncomingMessageAsRetrieverMessage(messageBody: String): Boolean {
|
||||
for (request in requests.values) {
|
||||
if (request.type == RETRIEVER) {
|
||||
// 11-digit hash code that uniquely identifies your app
|
||||
if (request.appHashString.isNullOrBlank() || !messageBody.contains(request.appHashString)) continue
|
||||
|
||||
sendRetrieverBroadcast(request, messageBody)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun tryHandleIncomingMessageAsUserConsentMessage(senderPhoneNumber: String?, messageBody: String): Boolean {
|
||||
val senderPhoneNumber = senderPhoneNumber ?: return false
|
||||
|
||||
// 4-10 digit alphanumeric code containing at least one number
|
||||
if (messageBody.split("[^A-Za-z0-9]".toRegex()).none { it.length in 4..10 && it.any(Char::isDigit) }) return false
|
||||
|
||||
// Sender cannot be in the user's Contacts list
|
||||
if (isPhoneNumberInContacts(context, senderPhoneNumber)) return false
|
||||
|
||||
for (request in requests.values) {
|
||||
if (request.type == USER_CONSENT) {
|
||||
if (!request.senderPhoneNumber.isNullOrBlank() && request.senderPhoneNumber != senderPhoneNumber) continue
|
||||
|
||||
sendUserConsentBroadcast(request, messageBody)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun handleIncomingSmsMessage(senderPhoneNumber: String?, messageBody: String) {
|
||||
Log.d(TAG, "handleIncomingSmsMessage: senderPhoneNumber:$senderPhoneNumber messageBody: $messageBody")
|
||||
if (messageBody.isBlank()) return
|
||||
|
||||
if (tryHandleIncomingMessageAsRetrieverMessage(messageBody)) return
|
||||
if (tryHandleIncomingMessageAsUserConsentMessage(senderPhoneNumber, messageBody)) return
|
||||
}
|
||||
|
||||
fun handleTimeout(requestId: Int) {
|
||||
val request = requests[requestId] ?: return
|
||||
sendReply(request, Status(CommonStatusCodes.TIMEOUT))
|
||||
}
|
||||
|
||||
private fun sendReply(request: SmsRetrieverRequest, status: Status, extras: Bundle = Bundle.EMPTY, finish: Boolean = true) {
|
||||
Log.d(TAG, "Send reply to ${request.packageName} ${CommonStatusCodes.getStatusCodeString(status.statusCode)}")
|
||||
|
||||
val intent = Intent(SmsRetriever.SMS_RETRIEVED_ACTION)
|
||||
intent.setPackage(request.packageName)
|
||||
intent.putExtras(extras)
|
||||
intent.putExtra(SmsRetriever.EXTRA_STATUS, status)
|
||||
context.sendBroadcast(intent)
|
||||
|
||||
if (finish) finishRequest(request)
|
||||
}
|
||||
|
||||
fun finishRequest(request: SmsRetrieverRequest) {
|
||||
alarmManager.cancel(request.timeoutPendingIntent)
|
||||
requests.remove(request.id)
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
super.onDestroy(owner)
|
||||
|
||||
if (this::smsBroadcastReceiver.isInitialized) context.unregisterReceiver(smsBroadcastReceiver)
|
||||
if (this::timeoutBroadcastReceiver.isInitialized) context.unregisterReceiver(timeoutBroadcastReceiver)
|
||||
|
||||
for (request in requests.values) {
|
||||
sendReply(request, Status(CommonStatusCodes.TIMEOUT))
|
||||
}
|
||||
|
||||
requests.clear()
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
private inner class SmsReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION == intent.action) {
|
||||
val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
|
||||
val messageBodyBuilder = StringBuilder()
|
||||
var senderPhoneNumber: String? = null
|
||||
for (message in messages) {
|
||||
messageBodyBuilder.append(message.messageBody)
|
||||
senderPhoneNumber = message.originatingAddress
|
||||
}
|
||||
try {
|
||||
handleIncomingSmsMessage(senderPhoneNumber, messageBodyBuilder.toString())
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Error handling incoming SMS", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class TimeoutReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val requestId = intent.getIntExtra(EXTRA_REQUEST_ID, -1)
|
||||
if (requestId != -1) {
|
||||
handleTimeout(requestId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
fun getHashString(packageName: String): String {
|
||||
val signature =
|
||||
context.packageManager.getSignatures(packageName).firstOrNull()?.toCharsString() ?: throw RuntimeException("No signature found for $packageName")
|
||||
val appInfo = "$packageName $signature"
|
||||
val messageDigest = MessageDigest.getInstance("SHA-256")
|
||||
messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8))
|
||||
return Base64.encodeToString(messageDigest.digest(), Base64.NO_PADDING or Base64.NO_WRAP).substring(0, 11)
|
||||
}
|
||||
|
||||
private fun anyOtherPackageHasHashString(packageName: String, hashString: String): Boolean {
|
||||
val collision = context.packageManager.getInstalledPackages(0)
|
||||
.firstOrNull { it.packageName != packageName && getHashString(it.packageName) == hashString } ?: return false
|
||||
|
||||
Log.w(TAG, "Hash string collision between $packageName and ${collision.packageName} (both are $hashString)")
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isPhoneNumberInContacts(context: Context, phoneNumber: String): Boolean {
|
||||
fun normalizePhoneNumber(input: String): String {
|
||||
var output = ""
|
||||
if (!TextUtils.isEmpty(input)) {
|
||||
// only keep digits
|
||||
val temp = input.replace("[^0-9]".toRegex(), "")
|
||||
// trim leading zeroes
|
||||
output = temp.replaceFirst("^0*".toRegex(), "")
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
val normalizePhoneNumber = normalizePhoneNumber(phoneNumber)
|
||||
var cursor: Cursor? = null
|
||||
try {
|
||||
cursor = context.contentResolver.query(Phone.CONTENT_URI, arrayOf(Phone.NUMBER), null, null, null) ?: return false
|
||||
while (cursor.moveToNext()) {
|
||||
val addressIndex = cursor.getColumnIndex(Phone.NUMBER)
|
||||
val contactPhoneNumber = normalizePhoneNumber(cursor.getString(addressIndex))
|
||||
if (!TextUtils.isEmpty(normalizePhoneNumber) && !TextUtils.isEmpty(contactPhoneNumber) && normalizePhoneNumber == contactPhoneNumber) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, e)
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private val activePermissionRequestLock = Mutex()
|
||||
private var activePermissionRequest: Deferred<Boolean>? = null
|
||||
|
||||
private suspend fun ensurePermission(permissions: Array<String>): Boolean {
|
||||
if (SDK_INT < 23)
|
||||
return true
|
||||
|
||||
if (permissions.all { ContextCompat.checkSelfPermission(context, it) == PERMISSION_GRANTED })
|
||||
return true
|
||||
|
||||
val (completable, deferred) = activePermissionRequestLock.withLock {
|
||||
if (activePermissionRequest == null) {
|
||||
val completable = CompletableDeferred<Boolean>()
|
||||
activePermissionRequest = completable
|
||||
completable to activePermissionRequest!!
|
||||
} else {
|
||||
null to activePermissionRequest!!
|
||||
}
|
||||
}
|
||||
if (completable != null) {
|
||||
val intent = Intent(context, AskPermissionActivity::class.java)
|
||||
intent.putExtra(EXTRA_MESSENGER, Messenger(object : Handler(Looper.getMainLooper()) {
|
||||
override fun handleMessage(msg: Message) {
|
||||
if (msg.what == Activity.RESULT_OK) {
|
||||
val grantResults = msg.data?.getIntArray(EXTRA_GRANT_RESULTS) ?: IntArray(0)
|
||||
completable.complete(grantResults.size == permissions.size && grantResults.all { it == PERMISSION_GRANTED })
|
||||
} else {
|
||||
completable.complete(false)
|
||||
}
|
||||
}
|
||||
}))
|
||||
intent.putExtra(EXTRA_PERMISSIONS, permissions)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
context.startActivity(intent)
|
||||
}
|
||||
return deferred.await()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.phone
|
||||
|
||||
import android.app.PendingIntent
|
||||
|
||||
data class SmsRetrieverRequest(
|
||||
val id: Int,
|
||||
val type: SmsRetrieverRequestType,
|
||||
val packageName: String,
|
||||
val timeoutPendingIntent: PendingIntent,
|
||||
val appHashString: String? = null,
|
||||
val creation: Long = System.currentTimeMillis(),
|
||||
val senderPhoneNumber: String? = null
|
||||
)
|
||||
|
||||
enum class SmsRetrieverRequestType {
|
||||
RETRIEVER, USER_CONSENT
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.phone
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.gms.auth.api.phone.SmsRetrieverStatusCodes
|
||||
import com.google.android.gms.auth.api.phone.internal.IAutofillPermissionStateCallback
|
||||
import com.google.android.gms.auth.api.phone.internal.IOngoingSmsRequestCallback
|
||||
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverApiService
|
||||
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverResultCallback
|
||||
import com.google.android.gms.common.Feature
|
||||
import com.google.android.gms.common.api.CommonStatusCodes
|
||||
import com.google.android.gms.common.api.Status
|
||||
import com.google.android.gms.common.api.internal.IStatusCallback
|
||||
import com.google.android.gms.common.internal.ConnectionInfo
|
||||
import com.google.android.gms.common.internal.GetServiceRequest
|
||||
import com.google.android.gms.common.internal.IGmsCallbacks
|
||||
import org.microg.gms.BaseService
|
||||
import org.microg.gms.common.GmsService
|
||||
import org.microg.gms.common.PackageUtils
|
||||
|
||||
|
||||
private const val TAG = "SmsRetrieverService"
|
||||
private val FEATURES = arrayOf(
|
||||
Feature("sms_retrieve", 1),
|
||||
Feature("user_consent", 3)
|
||||
)
|
||||
|
||||
class SmsRetrieverService : BaseService(TAG, GmsService.SMS_RETRIEVER) {
|
||||
private val smsRetriever = SmsRetrieverCore(this, lifecycle)
|
||||
|
||||
override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) {
|
||||
val packageName = PackageUtils.getAndCheckCallingPackage(this, request.packageName)
|
||||
?: throw IllegalArgumentException("Missing package name")
|
||||
callback.onPostInitCompleteWithConnectionInfo(
|
||||
CommonStatusCodes.SUCCESS,
|
||||
SmsRetrieverServiceImpl(smsRetriever, packageName, lifecycle),
|
||||
ConnectionInfo().apply { features = FEATURES }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SmsRetrieverServiceImpl(private val smsRetriever: SmsRetrieverCore, private val packageName: String, override val lifecycle: Lifecycle) :
|
||||
ISmsRetrieverApiService.Stub(), LifecycleOwner {
|
||||
|
||||
override fun startSmsRetriever(callback: ISmsRetrieverResultCallback) {
|
||||
Log.d(TAG, "startSmsRetriever()")
|
||||
lifecycleScope.launchWhenStarted {
|
||||
val status = try {
|
||||
smsRetriever.startSmsRetriever(packageName)
|
||||
Status.SUCCESS
|
||||
} catch (e: Exception) {
|
||||
Status(CommonStatusCodes.INTERNAL_ERROR, e.message)
|
||||
}
|
||||
try {
|
||||
callback.onResult(status)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed delivering $status for startSmsRetriever()", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun startWithConsentPrompt(senderPhoneNumber: String?, callback: ISmsRetrieverResultCallback) {
|
||||
Log.d(TAG, "startWithConsentPrompt($senderPhoneNumber)")
|
||||
lifecycleScope.launchWhenStarted {
|
||||
val status = try {
|
||||
smsRetriever.startWithConsentPrompt(packageName, senderPhoneNumber)
|
||||
Status.SUCCESS
|
||||
} catch (e: Exception) {
|
||||
Status(CommonStatusCodes.INTERNAL_ERROR, e.message)
|
||||
}
|
||||
try {
|
||||
callback.onResult(status)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed delivering $status for startWithConsentPrompt()", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun startSmsCodeAutofill(callback: IStatusCallback) {
|
||||
Log.d(TAG, "startSmsCodeAutofill()")
|
||||
try {
|
||||
callback.onResult(Status(SmsRetrieverStatusCodes.API_NOT_AVAILABLE))
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed delivering result for startSmsCodeAutofill()", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkAutofillPermissionState(callback: IAutofillPermissionStateCallback) {
|
||||
Log.d(TAG, "checkAutofillPermissionState()")
|
||||
try {
|
||||
callback.onCheckPermissionStateResult(Status.SUCCESS, 1)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed delivering result for checkAutofillPermissionState()", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkOngoingSmsRequest(packageName: String?, callback: IOngoingSmsRequestCallback) {
|
||||
Log.d(TAG, "checkOngoingSmsRequest($packageName)")
|
||||
lifecycleScope.launchWhenStarted {
|
||||
val result = try {
|
||||
smsRetriever.hasOngoingUserConsentRequest()
|
||||
} catch (e: Exception) {
|
||||
true
|
||||
}
|
||||
|
||||
try {
|
||||
callback.onHasOngoingSmsRequestResult(Status.SUCCESS, result)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed delivering $result for checkOngoingSmsRequest()", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun startSmsCodeBrowser(callback: IStatusCallback) {
|
||||
Log.d(TAG, "startSmsCodeBrowser()")
|
||||
try {
|
||||
callback.onResult(Status(SmsRetrieverStatusCodes.API_NOT_AVAILABLE))
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed delivering result for startSmsCodeBrowser()", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.phone
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Intent
|
||||
import android.os.*
|
||||
import android.text.Html
|
||||
import android.view.Gravity
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.gms.auth.api.phone.SmsRetriever
|
||||
import org.microg.gms.ui.buildAlertDialog
|
||||
import org.microg.gms.utils.getApplicationLabel
|
||||
|
||||
private const val TAG = "UserConsentPrompt"
|
||||
|
||||
class UserConsentPromptActivity : AppCompatActivity() {
|
||||
private val messenger: Messenger?
|
||||
get() = intent.getParcelableExtra(EXTRA_MESSENGER)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val callingPackage = callingActivity?.packageName ?: return finish()
|
||||
val messenger = messenger ?: return finish()
|
||||
messenger.send(Message.obtain().apply {
|
||||
what = MSG_REQUEST_MESSAGE_BODY
|
||||
replyTo = Messenger(object : Handler(Looper.getMainLooper()) {
|
||||
override fun handleMessage(msg: Message) {
|
||||
if (msg.what == MSG_REQUEST_MESSAGE_BODY) {
|
||||
val message = msg.data.getString("message") ?: return
|
||||
showConsentDialog(callingPackage, message)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private fun showConsentDialog(callingPackage: String, message: String) {
|
||||
val view = layoutInflater.inflate(R.layout.dialog_sms_user_consent, null)
|
||||
val dialog = buildAlertDialog()
|
||||
.setCancelable(false)
|
||||
.setView(view)
|
||||
.create()
|
||||
val appName = packageManager.getApplicationLabel(callingPackage)
|
||||
|
||||
view.findViewById<TextView>(android.R.id.title).text = Html.fromHtml(getString(R.string.sms_user_consent_title, Html.escapeHtml(appName)))
|
||||
view.findViewById<TextView>(android.R.id.text1).text = message
|
||||
view.findViewById<Button>(android.R.id.button2).setOnClickListener {
|
||||
dialog.cancel()
|
||||
}
|
||||
dialog.setOnCancelListener {
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
view.findViewById<Button>(android.R.id.button1).setOnClickListener {
|
||||
dialog.dismiss()
|
||||
setResult(RESULT_OK, Intent().apply {
|
||||
putExtra(SmsRetriever.EXTRA_SMS_MESSAGE, message)
|
||||
})
|
||||
messenger?.send(Message.obtain().apply {
|
||||
what = MSG_CONSUME_MESSAGE
|
||||
})
|
||||
finish()
|
||||
}
|
||||
if (!dialog.isShowing) {
|
||||
dialog.window?.setGravity(Gravity.BOTTOM)
|
||||
dialog.window?.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.phone
|
||||
|
||||
const val MSG_REQUEST_MESSAGE_BODY = 1
|
||||
const val MSG_CONSUME_MESSAGE = 2
|
||||
|
||||
const val EXTRA_MESSENGER = "messenger"
|
||||
const val EXTRA_PERMISSIONS = "permissions"
|
||||
const val EXTRA_GRANT_RESULTS = "grantResults"
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="bottom|center"
|
||||
android:padding="24dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="textStart"
|
||||
android:gravity="start"
|
||||
style="@style/TextAppearance.AppCompat.Title"
|
||||
tools:text="Allow App to read the message below and enter the code?" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="textStart"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="start"
|
||||
style="@style/TextAppearance.AppCompat.Small"
|
||||
tools:text="OTP is 123456 to accept transaction with App. Do not share for security reasons" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_gravity="end"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@android:id/button2"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@string/sms_user_consent_deny"
|
||||
style="@style/Widget.AppCompat.Button.Borderless" />
|
||||
|
||||
<Button
|
||||
android:id="@android:id/button1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/sms_user_consent_allow"
|
||||
style="@style/Widget.AppCompat.Button.Colored" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_deny">رفض</string>
|
||||
<string name="sms_user_consent_allow">سماح</string>
|
||||
<string name="sms_user_consent_title">السماح ل<b>%s</b> بقراءة الرسالة أدناه وإدخال الرمز؟</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">Permitir</string>
|
||||
<string name="sms_user_consent_title">¿Quies permitir que l\'aplicación <b>%s</b> llea\'l mensaxe d\'abaxo ya introduza\'l códigu\?</string>
|
||||
<string name="sms_user_consent_deny">Negar</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title"><b>%s</b>-ə aşağıdakı mesajı oxumağa və kodu daxil etməyə icazə verilsin?</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">Дазволіць <b>%s</b> прачытаць паведамленне і ўвесці код?</string>
|
||||
<string name="sms_user_consent_allow">Дазволіць</string>
|
||||
<string name="sms_user_consent_deny">Адхіліць</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_deny">Zakázat</string>
|
||||
<string name="sms_user_consent_title">Povolit aplikaci <b>%s</b> zobrazit zprávu níže a zadat kód?</string>
|
||||
<string name="sms_user_consent_allow">Povolit</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">Erlauben</string>
|
||||
<string name="sms_user_consent_deny">Ablehnen</string>
|
||||
<string name="sms_user_consent_title"><b>%s</b> erlauben, die folgende Nachricht zu lesen und den Code einzugeben?</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">¿Permitir que <b>%s</b> lean el mensaje de abajo e introduzcan el código?</string>
|
||||
<string name="sms_user_consent_allow">Permitir</string>
|
||||
<string name="sms_user_consent_deny">Denegar</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">پذیرفتن</string>
|
||||
<string name="sms_user_consent_deny">رد کردن</string>
|
||||
<string name="sms_user_consent_title">به <b>%s</b> اجازه دهید پیام زیر را بخواند و کد را وارد کنید؟</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">Sallitaanko <b>%s</b> lukea seuraavan viestin ja syöttää koodin?</string>
|
||||
<string name="sms_user_consent_allow">Salli</string>
|
||||
<string name="sms_user_consent_deny">Kiellä</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">Payagan ang <b>%s</b> na basahin ang mensahe sa ibaba at ilagay ang code?</string>
|
||||
<string name="sms_user_consent_allow">Payagan</string>
|
||||
<string name="sms_user_consent_deny">Tanggihan</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">Autoriser <b>%s</b> à lire le message ci-dessous et saisir le code ?</string>
|
||||
<string name="sms_user_consent_allow">Autoriser</string>
|
||||
<string name="sms_user_consent_deny">Refuser</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_deny">Diúltaigh</string>
|
||||
<string name="sms_user_consent_title">An bhfuil cead ag <b>%s</b> an teachtaireacht thíos a léamh agus an cód a chur isteach?</string>
|
||||
<string name="sms_user_consent_allow">Ceadaigh</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">Izinkan</string>
|
||||
<string name="sms_user_consent_title">Izinkan <b>%s</b> untuk membaca pesan di bawah ini dan memasukkan kode?</string>
|
||||
<string name="sms_user_consent_deny">Tolak</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">Leyfa <b>%s</b> að lesa skilaboðin hér fyrir neðan og setja inn kóðann?</string>
|
||||
<string name="sms_user_consent_deny">Hafna</string>
|
||||
<string name="sms_user_consent_allow">Leyfa</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">Consenti</string>
|
||||
<string name="sms_user_consent_title">Consentire a <b>%s</b> di leggere il seguente messaggio e inserire il codice\?</string>
|
||||
<string name="sms_user_consent_deny">Nega</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">許可</string>
|
||||
<string name="sms_user_consent_title"><b>%s</b> に以下のメッセージの読み取りを許可してコードを入力させますか?</string>
|
||||
<string name="sms_user_consent_deny">拒否</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title"><b>%s</b>가 아래의 메시지를 읽고 코드를 입력하도록 허용할까요?</string>
|
||||
<string name="sms_user_consent_allow">허용</string>
|
||||
<string name="sms_user_consent_deny">거부</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">Atļaut</string>
|
||||
<string name="sms_user_consent_deny">Liegt</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">താഴെയുള്ള സന്ദേശം വായിച്ച് കോഡ് നൽകാൻ <b>%s</b> നെ അനുവദിക്കണോ?</string>
|
||||
<string name="sms_user_consent_allow">അനുവദിക്കുക</string>
|
||||
<string name="sms_user_consent_deny">നിരസിക്കുക</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_deny">Nekt</string>
|
||||
<string name="sms_user_consent_title">La <b>%s</b> lese meldingen under og legge inn koden?</string>
|
||||
<string name="sms_user_consent_allow">Tillat</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title"><b>%s</b> toestaan het onderstaande bericht te lezen en de code in te voeren?</string>
|
||||
<string name="sms_user_consent_allow">Toestaan</string>
|
||||
<string name="sms_user_consent_deny">Niet toestaan</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">Zezwól</string>
|
||||
<string name="sms_user_consent_title">Zezwolić <b>%s</b> na odczyt poniższej wiadomości i wprowadzenie kodu?</string>
|
||||
<string name="sms_user_consent_deny">Odmów</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">Permitir que <b>%s</b> leia a mensagem abaixo e insira o código?</string>
|
||||
<string name="sms_user_consent_allow">Permitir</string>
|
||||
<string name="sms_user_consent_deny">Negar</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">Permitir que <b>%s</b> leia a mensagem abaixo e insira o código?</string>
|
||||
<string name="sms_user_consent_allow">Permitir</string>
|
||||
<string name="sms_user_consent_deny">Recusar</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">Permite</string>
|
||||
<string name="sms_user_consent_title">Permiți ca <b>%s</b> să citească mesajul de mai jos și să introducă codul\?</string>
|
||||
<string name="sms_user_consent_deny">Refuză</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">Разрешить <b>%s</b> прочитать сообщение ниже и ввести код?</string>
|
||||
<string name="sms_user_consent_allow">Разрешить</string>
|
||||
<string name="sms_user_consent_deny">Отклонить</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">Дозволи</string>
|
||||
<string name="sms_user_consent_title">Дозволити <b>%s</b> да прочита поруку испод и унесе кôд\?</string>
|
||||
<string name="sms_user_consent_deny">Одбиј</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">Tillåt</string>
|
||||
<string name="sms_user_consent_title">Vill du låta <b>%s</b> läsa meddelandet nedan och ange koden?</string>
|
||||
<string name="sms_user_consent_deny">Neka</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">கீழே உள்ள செய்தியைப் படித்து குறியீட்டை உள்ளிட <b>%s </b> ஐ அனுமதிக்கவா?</string>
|
||||
<string name="sms_user_consent_allow">இசைவு</string>
|
||||
<string name="sms_user_consent_deny">மறு</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">อนุญาตให้ <b>%s</b> อ่านข้อความด้านล่างและกรอกรหัส?</string>
|
||||
<string name="sms_user_consent_allow">อนุญาต</string>
|
||||
<string name="sms_user_consent_deny">ปฏิเสธ</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">İzin ver</string>
|
||||
<string name="sms_user_consent_title"><b>%s</b> uygulamasının aşağıdaki mesajı okumasına ve kodu girmesine izin veriyor musunuz?</string>
|
||||
<string name="sms_user_consent_deny">Reddet</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title"><b>%s</b> نىڭ تۆۋەندىكى مەزمۇننى ئوقۇپ ۋە كودنى كىرگۈزۈشىگە يول قويامدۇ؟</string>
|
||||
<string name="sms_user_consent_deny">رەت قىل</string>
|
||||
<string name="sms_user_consent_allow">يول قوي</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_allow">Дозволити</string>
|
||||
<string name="sms_user_consent_title">Дозволити <b>%s</b> прочитати наведене нижче повідомлення та ввести код?</string>
|
||||
<string name="sms_user_consent_deny">Відхилити</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_deny">Từ chối</string>
|
||||
<string name="sms_user_consent_title">Cho phép <b>%s</b> đọc tin nhắn bên dưới và nhập mã?</string>
|
||||
<string name="sms_user_consent_allow">Chấp nhận</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">允许<b>%s</b>读取以下消息并输入代码?</string>
|
||||
<string name="sms_user_consent_allow">允许</string>
|
||||
<string name="sms_user_consent_deny">拒绝</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">允許「<b>%s</b>」閱讀以下訊息並輸入代碼?</string>
|
||||
<string name="sms_user_consent_deny">拒絕</string>
|
||||
<string name="sms_user_consent_allow">允許</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="sms_user_consent_title">Allow <b>%s</b> to read the message below and enter the code?</string>
|
||||
<string name="sms_user_consent_allow">Allow</string>
|
||||
<string name="sms_user_consent_deny">Deny</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<manifest />
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.google.android.gms.auth.api.phone.internal;
|
||||
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
interface IAutofillPermissionStateCallback {
|
||||
void onCheckPermissionStateResult(in Status status, int result) = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.google.android.gms.auth.api.phone.internal;
|
||||
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
interface IOngoingSmsRequestCallback {
|
||||
void onHasOngoingSmsRequestResult(in Status status, boolean hasOngoingSmsRequest) = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.google.android.gms.auth.api.phone.internal;
|
||||
|
||||
import com.google.android.gms.auth.api.phone.internal.IAutofillPermissionStateCallback;
|
||||
import com.google.android.gms.auth.api.phone.internal.IOngoingSmsRequestCallback;
|
||||
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverResultCallback;
|
||||
import com.google.android.gms.common.api.internal.IStatusCallback;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
import java.lang.String;
|
||||
|
||||
interface ISmsRetrieverApiService {
|
||||
void startSmsRetriever(ISmsRetrieverResultCallback callback) = 0;
|
||||
void startWithConsentPrompt(String senderPhoneNumber, ISmsRetrieverResultCallback callback) = 1;
|
||||
void startSmsCodeAutofill(IStatusCallback callback) = 2;
|
||||
void checkAutofillPermissionState(IAutofillPermissionStateCallback callback) = 3;
|
||||
void checkOngoingSmsRequest(String packageName, IOngoingSmsRequestCallback callback) = 4;
|
||||
void startSmsCodeBrowser(IStatusCallback callback) = 5;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.google.android.gms.auth.api.phone.internal;
|
||||
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
interface ISmsRetrieverResultCallback {
|
||||
void onResult(in Status status) = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.auth.api.phone;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Handler;
|
||||
import androidx.annotation.IntDef;
|
||||
import com.google.android.gms.common.api.*;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* The interface for interacting with the SMS Code Autofill API. These methods are only supported on devices running Android P and later.
|
||||
* For devices that run versions earlier than Android P, all method calls return {@link SmsRetrieverStatusCodes#PLATFORM_NOT_SUPPORTED}.
|
||||
* <p>
|
||||
* Note: This interface works only for the current user-designated autofill service.
|
||||
* Any calls from non user-designated autofill services or other applications will fail with {@link SmsRetrieverStatusCodes#API_NOT_AVAILABLE}.
|
||||
*/
|
||||
public interface SmsCodeAutofillClient extends HasApiKey<Api.ApiOptions.NoOptions> {
|
||||
/**
|
||||
* Returns the {@link SmsCodeAutofillClient.PermissionState} of the current user-designated autofill service.
|
||||
* The result could be {@code NONE}, {@code GRANTED}, or {@code DENIED}.
|
||||
* <p>
|
||||
* The autofill service should check its permission state prior to showing the suggestion prompt for retrieving an SMS
|
||||
* verification code, because it will definitely fail on calling {@link #startSmsCodeRetriever()} in permission denied state.
|
||||
*/
|
||||
Task<@PermissionState Integer> checkPermissionState();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if there are requests from {@link SmsRetriever} in progress for the given package name.
|
||||
* <p>
|
||||
* The autofill service can check this method to avoid showing a suggestion prompt for retrieving an SMS verification code,
|
||||
* in case that a user app may already be retrieving the SMS verification code through {@link SmsRetriever}.
|
||||
* <p>
|
||||
* Note: This result does not include those requests from {@code SmsCodeAutofillClient}.
|
||||
*/
|
||||
Task<Boolean> hasOngoingSmsRequest(String packageName);
|
||||
|
||||
/**
|
||||
* Starts {@code SmsCodeRetriever}, which looks for an SMS verification code from messages recently received (up to 1 minute
|
||||
* prior). If there is no SMS verification code found from the SMS inbox, it waits for new incoming SMS messages until it
|
||||
* finds an SMS verification code or reaches the timeout (about 5 minutes).
|
||||
* <p>
|
||||
* The SMS verification code will be sent via a Broadcast Intent with {@link SmsCodeRetriever#SMS_CODE_RETRIEVED_ACTION}. This Intent contains
|
||||
* Extras with keys {@link SmsCodeRetriever#EXTRA_SMS_CODE} for the retrieved verification code as a {@code String}, and {@link SmsCodeRetriever#EXTRA_STATUS} for {@link Status} to
|
||||
* indicate {@code RESULT_SUCCESS}, {@code RESULT_TIMEOUT} or {@link SmsRetrieverStatusCodes}.
|
||||
* <p>
|
||||
* Note: Add {@link SmsRetriever#SEND_PERMISSION} in {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)} while
|
||||
* registering the receiver to detect that the broadcast intent is from the SMS Retriever.
|
||||
*/
|
||||
Task<Void> startSmsCodeRetriever();
|
||||
|
||||
/**
|
||||
* Permission states for the current user-designated autofill service. The initial state is {@code NONE} upon the first time using the
|
||||
* SMS Code Autofill API. This permission can be granted or denied through a consent dialog requested by the current
|
||||
* autofill service, or an explicit change by users within the SMS verification codes settings.
|
||||
*/
|
||||
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({PermissionState.NONE, PermissionState.GRANTED, PermissionState.DENIED})
|
||||
@interface PermissionState {
|
||||
/**
|
||||
* Indicates that the current autofill service has not been granted or denied permission by the user. Calling
|
||||
* {@link #startSmsCodeRetriever()} will fail with {@link CommonStatusCodes#RESOLUTION_REQUIRED}. The caller can use
|
||||
* {@link ResolvableApiException#startResolutionForResult(Activity, int)} to show a consent dialog for requesting permission from the user.
|
||||
*/
|
||||
int NONE = 0;
|
||||
/**
|
||||
* Indicates that the current autofill service has been granted permission by the user. The user consent is not required for
|
||||
* calling {@link #startSmsCodeRetriever()} in this state.
|
||||
*/
|
||||
int GRANTED = 1;
|
||||
/**
|
||||
* Indicates that the current autofill service has been denied permission by the user. Calling {@link #startSmsCodeRetriever()}
|
||||
* will fail with {@link SmsRetrieverStatusCodes#USER_PERMISSION_REQUIRED}. It can only be resolved by the user explicitly turning on the permission
|
||||
* in settings.
|
||||
*/
|
||||
int DENIED = 2;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.auth.api.phone;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Handler;
|
||||
import com.google.android.gms.common.api.Api;
|
||||
import com.google.android.gms.common.api.HasApiKey;
|
||||
import com.google.android.gms.common.api.ResolvableApiException;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
|
||||
/**
|
||||
* The interface for interacting with the SMS Code Browser API. By using {@link #startSmsCodeRetriever()}, you can retrieve the
|
||||
* origin-bound one-time code from SMS messages.
|
||||
* <p>
|
||||
* The SMS message format should follow the origin-bound one-time code specification:
|
||||
* <ul>
|
||||
* <li>Can optionally begin with human-readable explanatory text. This consists of all but the last line of the message.</li>
|
||||
* <li>The last line of the message contains both a host and a code, each prefixed with a sigil: U+0040 (@) before the host, and U+0023 (#) before the code.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Note: This interface works only for the default browser app set by the current user. Any other calls will fail with {@link SmsRetrieverStatusCodes#API_NOT_AVAILABLE}.
|
||||
*/
|
||||
public interface SmsCodeBrowserClient extends HasApiKey<Api.ApiOptions.NoOptions> {
|
||||
/**
|
||||
* Starts {@code SmsCodeRetriever}, which looks for an origin-bound one-time code from SMS messages recently received (up to
|
||||
* 1 minute prior). If there is no matching message found from the SMS inbox, it waits for new incoming SMS messages
|
||||
* until it finds a matching message or reaches the timeout (about 5 minutes). Calling this method multiple times only
|
||||
* returns one result, but it can extend the timeout period to the last call. Once the result is returned or it reaches
|
||||
* the timeout, SmsCodeRetriever will stop automatically.
|
||||
* <p>
|
||||
* The SMS verification code will be sent via a Broadcast Intent with {@link SmsCodeRetriever#SMS_CODE_RETRIEVED_ACTION}.
|
||||
* This Intent contains Extras with keys:
|
||||
* <ul>
|
||||
* <li>{@link SmsCodeRetriever#EXTRA_SMS_CODE_LINE} for the retrieved line that contains the origin-bound one-time code and the metadata, or
|
||||
* {@code null} in failed cases.</li>
|
||||
* <li>{@link SmsCodeRetriever#EXTRA_STATUS} for the Status to indicate {@code RESULT_SUCCESS}, {@code RESULT_TIMEOUT} or other {@link SmsRetrieverStatusCodes}.</li>
|
||||
* </ul>
|
||||
* If the caller has not been granted or denied permission by the user, it will fail with a {@link ResolvableApiException}. The
|
||||
* caller can use {@link ResolvableApiException#startResolutionForResult(Activity, int)} to show a consent dialog for requesting permission from
|
||||
* the user. The dialog result is returned via {@link Activity#onActivityResult(int, int, Intent)}. If the user grants the permission,
|
||||
* the activity result returns with {@code RESULT_OK}. Then you can start the retriever again to retrieve the verification code.
|
||||
* <p>
|
||||
* Note: Add {@link SmsRetriever#SEND_PERMISSION} in {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)} while
|
||||
* registering the receiver to detect that the broadcast intent is from the SMS Retriever.
|
||||
*/
|
||||
Task<Void> startSmsCodeRetriever();
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.auth.api.phone;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import org.microg.gms.auth.api.phone.SmsCodeAutofillClientImpl;
|
||||
import org.microg.gms.auth.api.phone.SmsCodeBrowserClientImpl;
|
||||
|
||||
/**
|
||||
* {@code SmsCodeRetriever} is a variant of {@link SmsRetriever}, and it provides access to Google services that help you retrieve SMS
|
||||
* verification codes sent to the user's device, without having to ask for {@code android.permission.READ_SMS} or {@code android.permission.RECEIVE_SMS}.
|
||||
* <p>
|
||||
* To use {@code SmsCodeRetriever} in the Android autofill service, obtain an instance of {@link SmsCodeAutofillClient} using
|
||||
* {@link #getAutofillClient(Context)} or {@link #getAutofillClient(Activity)}, and start SMS Code Retriever service by calling
|
||||
* {@link SmsCodeAutofillClient#startSmsCodeRetriever()}. To use it in the browser app, you obtain an instance of {@link SmsCodeBrowserClient} using
|
||||
* {@link #getBrowserClient(Context)} or {@link #getBrowserClient(Activity)} instead.
|
||||
* <p>
|
||||
* The service first looks for an SMS verification code from messages recently received (up to 1 minute prior). If there is no
|
||||
* SMS verification code found from the SMS inbox, it waits for new incoming SMS messages until it finds an SMS
|
||||
* verification code or reaches the timeout (about 5 minutes).
|
||||
*/
|
||||
public class SmsCodeRetriever {
|
||||
/**
|
||||
* Intent extra key of the retrieved SMS verification code by the {@link SmsCodeAutofillClient}.
|
||||
*/
|
||||
@NonNull
|
||||
public static final String EXTRA_SMS_CODE = "com.google.android.gms.auth.api.phone.EXTRA_SMS_CODE";
|
||||
/**
|
||||
* Intent extra key of the retrieved SMS verification code line by the {@link SmsCodeBrowserClient}.
|
||||
*/
|
||||
@NonNull
|
||||
public static final String EXTRA_SMS_CODE_LINE = "com.google.android.gms.auth.api.phone.EXTRA_SMS_CODE_LINE";
|
||||
/**
|
||||
* Intent extra key of {@link Status}, which indicates {@code RESULT_SUCCESS}, {@code RESULT_TIMEOUT} or {@link SmsRetrieverStatusCodes}.
|
||||
*/
|
||||
@NonNull
|
||||
public static final String EXTRA_STATUS = "com.google.android.gms.auth.api.phone.EXTRA_STATUS";
|
||||
/**
|
||||
* Intent action when an SMS verification code is retrieved.
|
||||
*/
|
||||
@NonNull
|
||||
public static final String SMS_CODE_RETRIEVED_ACTION = "com.google.android.gms.auth.api.phone.SMS_CODE_RETRIEVED";
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link SmsCodeAutofillClient} for use in an {@link Activity}.
|
||||
* This {@link SmsCodeAutofillClient} is intended to be used by the current user-designated autofill service only.
|
||||
*/
|
||||
@NonNull
|
||||
public static SmsCodeAutofillClient getAutofillClient(Activity activity) {
|
||||
return new SmsCodeAutofillClientImpl(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link SmsCodeAutofillClient} for use in a {@link Context}.
|
||||
* This {@link SmsCodeAutofillClient} is intended to be used by the current user-designated autofill service only.
|
||||
*/
|
||||
@NonNull
|
||||
public static SmsCodeAutofillClient getAutofillClient(Context context) {
|
||||
return new SmsCodeAutofillClientImpl(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link SmsCodeBrowserClient} for use in an {@link Activity}.
|
||||
* This {@link SmsCodeBrowserClient} is intended to be used by the default browser app only.
|
||||
*/
|
||||
@NonNull
|
||||
public static SmsCodeBrowserClient getBrowserClient(Activity activity) {
|
||||
return new SmsCodeBrowserClientImpl(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link SmsCodeBrowserClient} for use in a {@link Context}.
|
||||
* This {@link SmsCodeBrowserClient} is intended to be used by the default browser app only.
|
||||
*/
|
||||
@NonNull
|
||||
public static SmsCodeBrowserClient getBrowserClient(Context context) {
|
||||
return new SmsCodeBrowserClientImpl(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.auth.api.phone;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import org.microg.gms.auth.api.phone.SmsRetrieverClientImpl;
|
||||
|
||||
/**
|
||||
* {@code SmsRetriever} provides access to Google services that help you retrieve SMS messages sent to your app without
|
||||
* having to ask for {@code android.permission.READ_SMS} or {@code android.permission.RECEIVE_SMS}.
|
||||
* <p>
|
||||
* To use {@code SmsRetriever}, obtain an instance of {@link SmsRetrieverClient} using {@link #getClient(Context)} or
|
||||
* {@link #getClient(Activity)}, then start the SMS retriever service by calling {@link SmsRetrieverClient#startSmsRetriever()} or
|
||||
* {@link SmsRetrieverClient#startSmsUserConsent(String)}. The service waits for a matching SMS message until timeout (5 minutes).
|
||||
*/
|
||||
public class SmsRetriever {
|
||||
/**
|
||||
* Intent extra key of the consent intent to be launched from client app.
|
||||
*/
|
||||
@NonNull
|
||||
public static final String EXTRA_CONSENT_INTENT = "com.google.android.gms.auth.api.phone.EXTRA_CONSENT_INTENT";
|
||||
/**
|
||||
* [Optional] Intent extra key of the retrieved Sim card subscription Id if any, as an {@code int}.
|
||||
*/
|
||||
@NonNull
|
||||
public static final String EXTRA_SIM_SUBSCRIPTION_ID = "com.google.android.gms.auth.api.phone.EXTRA_SIM_SUBSCRIPTION_ID";
|
||||
/**
|
||||
* Intent extra key of the retrieved SMS message as a {@code String}.
|
||||
*/
|
||||
@NonNull
|
||||
public static final String EXTRA_SMS_MESSAGE = "com.google.android.gms.auth.api.phone.EXTRA_SMS_MESSAGE";
|
||||
/**
|
||||
* Intent extra key of {@link Status}, which indicates SUCCESS or TIMEOUT.
|
||||
*/
|
||||
@NonNull
|
||||
public static final String EXTRA_STATUS = "com.google.android.gms.auth.api.phone.EXTRA_STATUS";
|
||||
/**
|
||||
* Permission that's used to register the receiver to detect that the broadcaster is the SMS Retriever.
|
||||
*/
|
||||
@NonNull
|
||||
public static final String SEND_PERMISSION = "com.google.android.gms.auth.api.phone.permission.SEND";
|
||||
/**
|
||||
* Intent action when SMS message is retrieved.
|
||||
*/
|
||||
@NonNull
|
||||
public static final String SMS_RETRIEVED_ACTION = "com.google.android.gms.auth.api.phone.SMS_RETRIEVED";
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link SmsRetrieverClient} for use in an {@link Activity}.
|
||||
*/
|
||||
@NonNull
|
||||
public static SmsRetrieverClient getClient(Activity activity) {
|
||||
return new SmsRetrieverClientImpl(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link SmsRetrieverClient} for use in a {@link Context}.
|
||||
*/
|
||||
@NonNull
|
||||
public static SmsRetrieverClient getClient(Context context) {
|
||||
return new SmsRetrieverClientImpl(context);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.auth.api.phone;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.tasks.OnCompleteListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
|
||||
/**
|
||||
* API interface for SmsRetriever.
|
||||
*/
|
||||
public interface SmsRetrieverApi {
|
||||
/**
|
||||
* Starts {@code SmsRetriever}, which waits for a matching SMS message until timeout (5 minutes). The matching SMS message
|
||||
* will be sent via a Broadcast Intent with action {@link SmsRetriever#SMS_RETRIEVED_ACTION}. The Intent contains Extras with keys
|
||||
* {@link SmsRetriever#EXTRA_SMS_MESSAGE} for the retrieved SMS message as a String, and {@link SmsRetriever#EXTRA_STATUS} for {@link Status} to indicate
|
||||
* {@code SUCCESS}, {@code DEVELOPER_ERROR}, {@code ERROR}, or {@code TIMEOUT}.
|
||||
* <p>
|
||||
* Note: Add {@link SmsRetriever#SEND_PERMISSION} while registering the receiver to detect that the broadcast intent is from the SMS Retriever.
|
||||
* <p>
|
||||
* The possible causes for errors are:
|
||||
* <ul>
|
||||
* <li>DEVELOPER_ERROR: the caller app has incorrect number of certificates. Only one certificate is allowed.</li>
|
||||
* <li>ERROR: the AppCode collides with other installed apps.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return a Task for the call. Attach an {@link OnCompleteListener} and then check {@link Task#isSuccessful()} to determine if it was successful.
|
||||
*/
|
||||
@NonNull
|
||||
Task<Void> startSmsRetriever();
|
||||
|
||||
/**
|
||||
* Starts {@code SmsUserConsent}, which waits for an OTP-containing SMS message until timeout (5 minutes). OTP-containing
|
||||
* SMS message can be retrieved with two steps.
|
||||
* <p>
|
||||
* Note: Add {@link SmsRetriever#SEND_PERMISSION} while registering the receiver to detect that the broadcast intent is from the SMS Retriever.
|
||||
* <ol>
|
||||
* <li>[Get consent Intent] While OTP-containing SMS message comes, a consent Intent will be sent via a Broadcast
|
||||
* Intent with action {@link SmsRetriever#SMS_RETRIEVED_ACTION}. The Intent contains Extras with keys {@link SmsRetriever#EXTRA_CONSENT_INTENT} for the
|
||||
* consent Intent and {@link SmsRetriever#EXTRA_STATUS} for {@link Status} to indicate {@code SUCCESS} or {@code TIMEOUT}.</li>
|
||||
* <li>[Get OTP-containing SMS message] Calls {@code startActivityForResult} with consent Intent to launch a consent
|
||||
* dialog to get user's approval, then the OTP-containing SMS message can be retrieved from the activity result.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param senderAddress address of desired SMS sender, or {@code null} to retrieve any sender
|
||||
* @return a Task for the call. Attach an {@link OnCompleteListener} and then check {@link Task#isSuccessful()} to determine if it was successful.
|
||||
*/
|
||||
@NonNull
|
||||
Task<Void> startSmsUserConsent(@Nullable String senderAddress);
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.auth.api.phone;
|
||||
|
||||
import android.content.Context;
|
||||
import com.google.android.gms.common.api.Api;
|
||||
import com.google.android.gms.common.api.GoogleApi;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import org.microg.gms.auth.api.phone.SmsRetrieverApiClient;
|
||||
|
||||
/**
|
||||
* The main entry point for interacting with SmsRetriever.
|
||||
* <p>
|
||||
* This does not require a {@link GoogleApiClient}. See {@link GoogleApi} for more information.
|
||||
*/
|
||||
public abstract class SmsRetrieverClient extends GoogleApi<Api.ApiOptions.NoOptions> implements SmsRetrieverApi {
|
||||
|
||||
protected SmsRetrieverClient(Context context) {
|
||||
super(context, SmsRetrieverApiClient.API, Api.ApiOptions.NO_OPTIONS);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.auth.api.phone;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.android.gms.common.api.CommonStatusCodes;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
/**
|
||||
* SMS Retriever specific status codes, for use in {@link Status#getStatusCode()}.
|
||||
*/
|
||||
public class SmsRetrieverStatusCodes extends CommonStatusCodes {
|
||||
/**
|
||||
* The current Android platform does not support this particular API.
|
||||
*/
|
||||
public static final int PLATFORM_NOT_SUPPORTED = 36500;
|
||||
/**
|
||||
* The calling application is not eligible to use this particular API.
|
||||
* <p>
|
||||
* Note: For {@link SmsCodeAutofillClient}, this status indicates that the calling application is not the current user-designated
|
||||
* autofill service. For {@link SmsCodeBrowserClient}, it indicates that the caller is not the system default browser app.
|
||||
*/
|
||||
public static final int API_NOT_AVAILABLE = 36501;
|
||||
/**
|
||||
* The user has not granted the calling application permission to use this particular API.
|
||||
*/
|
||||
public static final int USER_PERMISSION_REQUIRED = 36502;
|
||||
|
||||
/**
|
||||
* Returns an untranslated debug string based on the given status code.
|
||||
*/
|
||||
@NonNull
|
||||
public static String getStatusCodeString(int statusCode) {
|
||||
switch (statusCode) {
|
||||
case PLATFORM_NOT_SUPPORTED:
|
||||
return "PLATFORM_NOT_SUPPORTED";
|
||||
case API_NOT_AVAILABLE:
|
||||
return "API_NOT_AVAILABLE";
|
||||
case USER_PERMISSION_REQUIRED:
|
||||
return "USER_PERMISSION_REQUIRED";
|
||||
default:
|
||||
return CommonStatusCodes.getStatusCodeString(statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
/**
|
||||
* {@code SmsRetriever} contains two APIs, the SMS Retriever API and the SMS User Consent API, that provide access to Google
|
||||
* services that help you retrieve SMS messages directed to your app, without having to ask for
|
||||
* {@code android.permission.READ_SMS} or {@code android.permission.RECEIVE_SMS}. The {@code SmsCodeRetriever} is for autofill
|
||||
* services and browser apps to retrieve SMS-based verification codes.
|
||||
* <p>
|
||||
* Many apps use phone numbers to verify the identity of a user. The app sends an SMS message containing an OTP (One
|
||||
* Time Passcode) to the user, who then enters the OTP from the received SMS message to prove ownership of the phone number.
|
||||
* <p>
|
||||
* In Android, to provide a streamlined UX, an app may request the SMS read permission, and retrieve the OTP
|
||||
* automatically. This is problematic since this permission allows the app to read other SMS messages which may contain
|
||||
* the user's private information. Also, the latest Play Store policy changes restrict access to SMS messages.
|
||||
* <p>
|
||||
* The SMS Retriever API solves this problem by providing app developers a way to automatically retrieve only the SMS
|
||||
* directed to the app without asking for the SMS read permission or gaining the ability to read any other SMS messages on the device.
|
||||
* <p>
|
||||
* The SMS User Consent API complements the SMS Retriever API by allowing an app to prompt the user to grant access to
|
||||
* the content of the next SMS message that contains an OTP. When a user gives consent, the app will then have access to
|
||||
* the entire message body to automatically complete SMS verification.
|
||||
* <p>
|
||||
* The SMS Retriever API completely automates the SMS-based OTP verification process for the user. However, there are
|
||||
* situations where you don’t control the format of the SMS message and as a result cannot use the SMS Retriever API.
|
||||
* In these situations, you can use the SMS User Consent API to streamline the process.
|
||||
* <p>
|
||||
* With the SMS Code Autofill API, a user-designated autofill service can retrieve the SMS verification codes from the SMS
|
||||
* inbox or new incoming SMS messages, then fill in this code for a user to complete any SMS verification requests in a
|
||||
* user app. For browser apps, you can achieve this by using the SMS Code Browser API.
|
||||
*/
|
||||
package com.google.android.gms.auth.api.phone;
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.api.phone;
|
||||
|
||||
import android.content.Context;
|
||||
import com.google.android.gms.auth.api.phone.SmsCodeAutofillClient;
|
||||
import com.google.android.gms.auth.api.phone.internal.IAutofillPermissionStateCallback;
|
||||
import com.google.android.gms.auth.api.phone.internal.IOngoingSmsRequestCallback;
|
||||
import com.google.android.gms.common.api.Api;
|
||||
import com.google.android.gms.common.api.ApiException;
|
||||
import com.google.android.gms.common.api.GoogleApi;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import org.microg.gms.common.api.PendingGoogleApiCall;
|
||||
|
||||
public class SmsCodeAutofillClientImpl extends GoogleApi<Api.ApiOptions.NoOptions> implements SmsCodeAutofillClient {
|
||||
public SmsCodeAutofillClientImpl(Context context) {
|
||||
super(context, SmsRetrieverApiClient.API, Api.ApiOptions.NO_OPTIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<@PermissionState Integer> checkPermissionState() {
|
||||
return scheduleTask((PendingGoogleApiCall<Integer, SmsRetrieverApiClient>) (client, completionSource) -> client.checkAutofillPermissionState(new IAutofillPermissionStateCallback.Stub() {
|
||||
@Override
|
||||
public void onCheckPermissionStateResult(Status status, int result) {
|
||||
if (status.isSuccess()) {
|
||||
completionSource.trySetResult(result);
|
||||
} else {
|
||||
completionSource.trySetException(new ApiException(status));
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<Boolean> hasOngoingSmsRequest(String packageName) {
|
||||
return scheduleTask((PendingGoogleApiCall<Boolean, SmsRetrieverApiClient>) (client, completionSource) -> client.checkOngoingSmsRequest(packageName, new IOngoingSmsRequestCallback.Stub() {
|
||||
@Override
|
||||
public void onHasOngoingSmsRequestResult(Status status, boolean result) {
|
||||
if (status.isSuccess()) {
|
||||
completionSource.trySetResult(result);
|
||||
} else {
|
||||
completionSource.trySetException(new ApiException(status));
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<Void> startSmsCodeRetriever() {
|
||||
return scheduleTask((PendingGoogleApiCall<Void, SmsRetrieverApiClient>) (client, completionSource) -> client.startSmsCodeAutofill(new StatusCallbackImpl(completionSource)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.api.phone;
|
||||
|
||||
import android.content.Context;
|
||||
import com.google.android.gms.auth.api.phone.SmsCodeBrowserClient;
|
||||
import com.google.android.gms.common.api.Api;
|
||||
import com.google.android.gms.common.api.GoogleApi;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import org.microg.gms.common.api.PendingGoogleApiCall;
|
||||
|
||||
public class SmsCodeBrowserClientImpl extends GoogleApi<Api.ApiOptions.NoOptions> implements SmsCodeBrowserClient {
|
||||
public SmsCodeBrowserClientImpl(Context context) {
|
||||
super(context, SmsRetrieverApiClient.API, Api.ApiOptions.NO_OPTIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<Void> startSmsCodeRetriever() {
|
||||
return scheduleTask((PendingGoogleApiCall<Void, SmsRetrieverApiClient>) (client, completionSource) -> client.startSmsCodeBrowser(new StatusCallbackImpl(completionSource)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.api.phone;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.gms.auth.api.phone.internal.IAutofillPermissionStateCallback;
|
||||
import com.google.android.gms.auth.api.phone.internal.IOngoingSmsRequestCallback;
|
||||
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverApiService;
|
||||
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverResultCallback;
|
||||
import com.google.android.gms.common.api.Api;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.common.api.internal.IStatusCallback;
|
||||
import org.microg.gms.common.GmsClient;
|
||||
import org.microg.gms.common.GmsService;
|
||||
import com.google.android.gms.common.api.internal.ConnectionCallbacks;
|
||||
import com.google.android.gms.common.api.internal.OnConnectionFailedListener;
|
||||
|
||||
public class SmsRetrieverApiClient extends GmsClient<ISmsRetrieverApiService> {
|
||||
public static final Api<Api.ApiOptions.NoOptions> API = new Api<>((options, context, looper, clientSettings, callbacks, connectionFailedListener) -> new SmsRetrieverApiClient(context, callbacks, connectionFailedListener));
|
||||
|
||||
public SmsRetrieverApiClient(Context context, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) {
|
||||
super(context, callbacks, connectionFailedListener, GmsService.SMS_RETRIEVER.ACTION);
|
||||
serviceId = GmsService.SMS_RETRIEVER.SERVICE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ISmsRetrieverApiService interfaceFromBinder(IBinder binder) {
|
||||
return ISmsRetrieverApiService.Stub.asInterface(binder);
|
||||
}
|
||||
|
||||
public void startSmsRetriever(ISmsRetrieverResultCallback callback) {
|
||||
try {
|
||||
getServiceInterface().startSmsRetriever(callback);
|
||||
} catch (RemoteException e) {
|
||||
try {
|
||||
callback.onResult(Status.INTERNAL_ERROR);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startWithConsentPrompt(@Nullable String senderAddress, ISmsRetrieverResultCallback callback) {
|
||||
try {
|
||||
getServiceInterface().startWithConsentPrompt(senderAddress, callback);
|
||||
} catch (RemoteException e) {
|
||||
try {
|
||||
callback.onResult(Status.INTERNAL_ERROR);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startSmsCodeAutofill(IStatusCallback callback) {
|
||||
try {
|
||||
getServiceInterface().startSmsCodeAutofill(callback);
|
||||
} catch (RemoteException e) {
|
||||
try {
|
||||
callback.onResult(Status.INTERNAL_ERROR);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkAutofillPermissionState(IAutofillPermissionStateCallback callback) {
|
||||
try {
|
||||
getServiceInterface().checkAutofillPermissionState(callback);
|
||||
} catch (RemoteException e) {
|
||||
try {
|
||||
callback.onCheckPermissionStateResult(Status.INTERNAL_ERROR, -1);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkOngoingSmsRequest(String packageName, IOngoingSmsRequestCallback callback) {
|
||||
try {
|
||||
getServiceInterface().checkOngoingSmsRequest(packageName, callback);
|
||||
} catch (RemoteException e) {
|
||||
try {
|
||||
callback.onHasOngoingSmsRequestResult(Status.INTERNAL_ERROR, false);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startSmsCodeBrowser(IStatusCallback callback) {
|
||||
try {
|
||||
getServiceInterface().startSmsCodeBrowser(callback);
|
||||
} catch (RemoteException e) {
|
||||
try {
|
||||
callback.onResult(Status.INTERNAL_ERROR);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.api.phone;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import org.microg.gms.common.api.PendingGoogleApiCall;
|
||||
|
||||
public class SmsRetrieverClientImpl extends SmsRetrieverClient {
|
||||
public SmsRetrieverClientImpl(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Task<Void> startSmsRetriever() {
|
||||
return scheduleTask((PendingGoogleApiCall<Void, SmsRetrieverApiClient>) (client, completionSource) -> client.startSmsRetriever(new SmsRetrieverResultCallbackImpl(completionSource)));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Task<Void> startSmsUserConsent(@Nullable String senderAddress) {
|
||||
return scheduleTask((PendingGoogleApiCall<Void, SmsRetrieverApiClient>) (client, completionSource) -> client.startWithConsentPrompt(senderAddress, new SmsRetrieverResultCallbackImpl(completionSource)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.api.phone;
|
||||
|
||||
import com.google.android.gms.auth.api.phone.internal.ISmsRetrieverResultCallback;
|
||||
import com.google.android.gms.common.api.ApiException;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.tasks.TaskCompletionSource;
|
||||
|
||||
class SmsRetrieverResultCallbackImpl extends ISmsRetrieverResultCallback.Stub {
|
||||
private final TaskCompletionSource<Void> completionSource;
|
||||
|
||||
public SmsRetrieverResultCallbackImpl(TaskCompletionSource<Void> completionSource) {
|
||||
this.completionSource = completionSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResult(Status status) {
|
||||
if (status.isSuccess()) {
|
||||
completionSource.trySetResult(null);
|
||||
} else {
|
||||
completionSource.trySetException(new ApiException(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.auth.api.phone;
|
||||
|
||||
import com.google.android.gms.common.api.ApiException;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.common.api.internal.IStatusCallback;
|
||||
import com.google.android.gms.tasks.TaskCompletionSource;
|
||||
|
||||
class StatusCallbackImpl extends IStatusCallback.Stub {
|
||||
private final TaskCompletionSource<Void> completionSource;
|
||||
|
||||
public StatusCallbackImpl(TaskCompletionSource<Void> completionSource) {
|
||||
this.completionSource = completionSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResult(Status status) {
|
||||
if (status.isSuccess()) {
|
||||
completionSource.trySetResult(null);
|
||||
} else {
|
||||
completionSource.trySetException(new ApiException(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue