diff --git a/.fvmrc b/.fvmrc index 37f94f6..e8b4151 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.19.0" + "flutter": "3.35.7" } \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index 388e27d..f9b3034 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1 +1 @@ -include: package:surf_lint_rules/analysis_options.yaml +include: package:flutter_lints/flutter.yaml diff --git a/android/build.gradle b/android/build.gradle index 64be700..89c3ee1 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,14 +2,14 @@ group 'ru.surfstudio.otp_autofill' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.9.23' + ext.kotlin_version = '2.0.21' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.5.1' + classpath 'com.android.tools.build:gradle:8.7.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -29,15 +29,15 @@ android { namespace 'ru.surfstudio.otp_autofill' } - compileSdkVersion 33 + compileSdkVersion 36 compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } sourceSets { @@ -50,9 +50,9 @@ android { } dependencies { - implementation 'com.google.android.gms:play-services-auth:20.3.0' - implementation 'com.google.android.gms:play-services-auth-api-phone:17.4.0' - implementation "androidx.activity:activity:1.5.1" - implementation "androidx.fragment:fragment:1.5.4" + implementation 'com.google.android.gms:play-services-auth:21.2.0' + implementation 'com.google.android.gms:play-services-auth-api-phone:18.1.0' + implementation "androidx.activity:activity:1.9.3" + implementation "androidx.fragment:fragment:1.8.5" } } \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 15de902..09523c0 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/src/main/kotlin/ru/surfstudio/otp_autofill/AppSignatureHelper.kt b/android/src/main/kotlin/ru/surfstudio/otp_autofill/AppSignatureHelper.kt index 1e6a233..17471b4 100644 --- a/android/src/main/kotlin/ru/surfstudio/otp_autofill/AppSignatureHelper.kt +++ b/android/src/main/kotlin/ru/surfstudio/otp_autofill/AppSignatureHelper.kt @@ -3,6 +3,7 @@ package ru.surfstudio.otp_autofill import android.content.Context import android.content.ContextWrapper import android.content.pm.PackageManager +import android.os.Build import android.util.Base64 import java.nio.charset.StandardCharsets import java.security.MessageDigest @@ -20,9 +21,24 @@ class AppSignatureHelper(context: Context) : ContextWrapper(context) { return try { val packageName = packageName val packageManager = packageManager - val signatures = packageManager.getPackageInfo(packageName, - PackageManager.GET_SIGNATURES).signatures - signatures.mapNotNull { hash(packageName, it.toCharsString()) } + val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + val signingInfo = packageManager.getPackageInfo( + packageName, + PackageManager.GET_SIGNING_CERTIFICATES + ).signingInfo + if (signingInfo?.hasMultipleSigners() == true) { + signingInfo.apkContentsSigners + } else { + signingInfo?.signingCertificateHistory + } + } else { + @Suppress("DEPRECATION") + packageManager.getPackageInfo( + packageName, + PackageManager.GET_SIGNATURES + ).signatures + } + signatures?.mapNotNull { hash(packageName, it.toCharsString()) } ?: emptyList() } catch (e: PackageManager.NameNotFoundException) { emptyList() } diff --git a/android/src/main/kotlin/ru/surfstudio/otp_autofill/OTPPlugin.kt b/android/src/main/kotlin/ru/surfstudio/otp_autofill/OTPPlugin.kt index e19e082..a9b89c4 100644 --- a/android/src/main/kotlin/ru/surfstudio/otp_autofill/OTPPlugin.kt +++ b/android/src/main/kotlin/ru/surfstudio/otp_autofill/OTPPlugin.kt @@ -124,6 +124,8 @@ class OTPPlugin : FlutterPlugin, MethodCallHandler, PluginRegistry.ActivityResul lastResult = null } else { // Consent denied. User can type OTC manually. + lastResult?.error("USER_DENIED", "User denied SMS consent", null) + lastResult = null } credentialPickerRequest -> if (resultCode == Activity.RESULT_OK && data != null) { diff --git a/android/src/main/kotlin/ru/surfstudio/otp_autofill/SmsRetrieverReceiver.kt b/android/src/main/kotlin/ru/surfstudio/otp_autofill/SmsRetrieverReceiver.kt index 4952430..2a3dd1c 100644 --- a/android/src/main/kotlin/ru/surfstudio/otp_autofill/SmsRetrieverReceiver.kt +++ b/android/src/main/kotlin/ru/surfstudio/otp_autofill/SmsRetrieverReceiver.kt @@ -16,11 +16,11 @@ class SmsRetrieverReceiver : BroadcastReceiver() { if (intent?.action == SmsRetriever.SMS_RETRIEVED_ACTION) { val extras = intent.extras - val smsRetrieverStatus = extras?.get(SmsRetriever.EXTRA_STATUS) as Status + val smsRetrieverStatus = extras?.get(SmsRetriever.EXTRA_STATUS) as? Status ?: return when (smsRetrieverStatus.statusCode) { CommonStatusCodes.SUCCESS -> { - extras.get(SmsRetriever.EXTRA_SMS_MESSAGE)?.also { + extras?.get(SmsRetriever.EXTRA_SMS_MESSAGE)?.also { smsBroadcastReceiverListener.onSuccess(it as String) } } diff --git a/android/src/main/kotlin/ru/surfstudio/otp_autofill/SmsUserConsentReceiver.kt b/android/src/main/kotlin/ru/surfstudio/otp_autofill/SmsUserConsentReceiver.kt index 605ea5e..6c17d81 100644 --- a/android/src/main/kotlin/ru/surfstudio/otp_autofill/SmsUserConsentReceiver.kt +++ b/android/src/main/kotlin/ru/surfstudio/otp_autofill/SmsUserConsentReceiver.kt @@ -3,6 +3,7 @@ package ru.surfstudio.otp_autofill import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.os.Build 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 @@ -16,11 +17,17 @@ class SmsUserConsentReceiver : BroadcastReceiver() { if (intent?.action == SmsRetriever.SMS_RETRIEVED_ACTION) { val extras = intent.extras - val smsRetrieverStatus = extras?.get(SmsRetriever.EXTRA_STATUS) as Status + val smsRetrieverStatus = extras?.get(SmsRetriever.EXTRA_STATUS) as? Status ?: return when (smsRetrieverStatus.statusCode) { CommonStatusCodes.SUCCESS -> { - extras.getParcelable(SmsRetriever.EXTRA_CONSENT_INTENT)?.also { + val consentIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + extras?.getParcelable(SmsRetriever.EXTRA_CONSENT_INTENT, Intent::class.java) + } else { + @Suppress("DEPRECATION") + extras?.getParcelable(SmsRetriever.EXTRA_CONSENT_INTENT) + } + consentIntent?.also { smsBroadcastReceiverListener.onSuccess(it) } } diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 388e27d..f9b3034 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -1 +1 @@ -include: package:surf_lint_rules/analysis_options.yaml +include: package:flutter_lints/flutter.yaml diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index bf0568f..aa4bd11 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -28,12 +28,12 @@ android { ndkVersion flutter.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } sourceSets { diff --git a/example/android/build.gradle b/example/android/build.gradle index 6c10361..cff2187 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.8.22' + ext.kotlin_version = '2.0.21' repositories { google() mavenCentral() diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 8bc9958..fd29b43 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue Nov 25 14:25:48 CET 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip diff --git a/example/lib/main.dart b/example/lib/main.dart index 014e015..8e24176 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -26,7 +26,7 @@ void main() { } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({super.key}); @override _MyAppState createState() => _MyAppState(); diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 7094767..7f31aa9 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: dev_dependencies: - surf_lint_rules: ^2.0.0 + flutter_lints: ^5.0.0 flutter: uses-material-design: true diff --git a/lib/src/otp_text_edit_controller.dart b/lib/src/otp_text_edit_controller.dart index 538e3a1..5516be3 100644 --- a/lib/src/otp_text_edit_controller.dart +++ b/lib/src/otp_text_edit_controller.dart @@ -192,4 +192,13 @@ class OTPTextEditController extends TextEditingController { void checkForComplete() { if (text.length == codeLength) onCodeReceive?.call(text); } + + @override + void dispose() { + // Remove the listener added in constructor + removeListener(checkForComplete); + // Stop listening for OTP codes and unregister broadcast receivers + stopListen(); + super.dispose(); + } } diff --git a/pubspec.yaml b/pubspec.yaml index 319e26f..0d70d9f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,12 +9,13 @@ dependencies: sdk: flutter dev_dependencies: + melos: 6.3.1 dependency_validator: ^3.0.0 flutter_test: sdk: flutter mocktail: ^0.2.0 - surf_lint_rules: ^2.0.0 + flutter_lints: ^5.0.0 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/test/otp_text_edit_controller_test.dart b/test/otp_text_edit_controller_test.dart index ebef5d9..da4b9b1 100644 --- a/test/otp_text_edit_controller_test.dart +++ b/test/otp_text_edit_controller_test.dart @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'dart:async'; + import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:otp_autofill/otp_autofill.dart'; import 'package:otp_autofill/src/util/platform_wrapper.dart'; -import 'package:surf_lint_rules/surf_lint_rules.dart'; const testCode = '54321'; const codeFromTestStrategyFirst = '23451'; @@ -64,8 +65,8 @@ void main() { codeOnCodeReceive = code; }, platform: platformWrapper, - onTimeOutException: onTimeOutException, - errorHandler: onException, + onTimeOutException: onTimeOutException.call, + errorHandler: onException.call, ); });