diff --git a/android/app/agconnect-services.json b/android/app/agconnect-services.json
new file mode 100644
index 00000000..8ca899db
--- /dev/null
+++ b/android/app/agconnect-services.json
@@ -0,0 +1 @@
+{"agcgw":{"url":"connect-drru.hispace.dbankcloud.ru","backurl":"connect-drru.hispace.dbankcloud.cn","websocketurl":"connect-ws-drru.hispace.dbankcloud.ru","websocketbackurl":"connect-ws-drru.hispace.dbankcloud.cn"},"agcgw_all":{"SG":"connect-dra.dbankcloud.cn","SG_back":"connect-dra.hispace.hicloud.com","CN":"connect-drcn.dbankcloud.cn","CN_back":"connect-drcn.hispace.hicloud.com","RU":"connect-drru.hispace.dbankcloud.ru","RU_back":"connect-drru.hispace.dbankcloud.cn","DE":"connect-dre.dbankcloud.cn","DE_back":"connect-dre.hispace.hicloud.com"},"websocketgw_all":{"SG":"connect-ws-dra.hispace.dbankcloud.cn","SG_back":"connect-ws-dra.hispace.dbankcloud.com","CN":"connect-ws-drcn.hispace.dbankcloud.cn","CN_back":"connect-ws-drcn.hispace.dbankcloud.com","RU":"connect-ws-drru.hispace.dbankcloud.ru","RU_back":"connect-ws-drru.hispace.dbankcloud.cn","DE":"connect-ws-dre.hispace.dbankcloud.cn","DE_back":"connect-ws-dre.hispace.dbankcloud.com"},"client":{"cp_id":"20007000036693451","product_id":"461323198429446894","client_id":"1595824543740483264","client_secret":"2A1727B0D31AA9BE1F5D9E8A5CDA1A67B9EF084AC9FB68D6C633A3CCB08ECDEE","project_id":"461323198429446894","app_id":"113238569","api_key":"DQEDAJNRITp9M3uc1rPHyT8PzLc7bnVcKJAeavZZk0TQNMx9ifvBebr+JOmZe9tEvjnLwv7c1hAHXmPGR/gWG86iABDrCVotq+NGnw==","package_name":"com.eatvioapp.app"},"oauth_client":{"client_id":"113238569","client_type":1},"app_info":{"app_id":"113238569","package_name":"com.eatvioapp.app"},"service":{"analytics":{"collector_url":"datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com","collector_url_cn":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn","collector_url_de":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn","collector_url_ru":"datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com","collector_url_sg":"datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn","resource_id":"p1","channel_id":""},"ml":{"mlservice_url":"ml-api-drru.ai.dbankcloud.ru,ml-api-drru.ai.dbankcloud.cn"},"cloudstorage":{"storage_url":"https://agc-storage-drcn.platform.dbankcloud.cn","storage_url_ru":"https://agc-storage-drru.cloud.huawei.ru","storage_url_sg":"https://ops-dra.agcstorage.link","storage_url_de":"https://ops-dre.agcstorage.link","storage_url_cn":"https://agc-storage-drcn.platform.dbankcloud.cn","storage_url_ru_back":"https://agc-storage-drru.cloud.huawei.ru","storage_url_sg_back":"https://agc-storage-dra.cloud.huawei.asia","storage_url_de_back":"https://agc-storage-dre.cloud.huawei.eu","storage_url_cn_back":"https://agc-storage-drcn.cloud.huawei.com.cn"},"search":{"url":"https://search-drru.cloud.huawei.ru"},"edukit":{"edu_url":"edukit.cloud.huawei.com.cn","dh_url":"edukit.cloud.huawei.com.cn"}},"region":"RU","configuration_version":"3.0","appInfos":[{"package_name":"com.eatvioapp.app","client":{"app_id":"113238569"},"app_info":{"package_name":"com.eatvioapp.app","app_id":"113238569"},"oauth_client":{"client_type":1,"client_id":"113238569"}}]}
\ No newline at end of file
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 457960f7..15095cb5 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -5,6 +5,7 @@ plugins {
// END: FlutterFire Configuration
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
+ id 'com.huawei.agconnect'
}
def keystoreProperties = new Properties()
@@ -35,7 +36,10 @@ android {
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
- manifestPlaceholders = [YANDEX_CLIENT_ID: yandexClientId]
+ manifestPlaceholders = [
+ YANDEX_CLIENT_ID: yandexClientId,
+ applicationName: "io.flutter.app.FlutterApplication"
+ ]
}
signingConfigs {
@@ -50,6 +54,9 @@ android {
buildTypes {
release {
signingConfig signingConfigs.release
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
signingConfig signingConfigs.debug
@@ -60,6 +67,14 @@ android {
}
}
+dependencies {
+ implementation 'com.huawei.hms:push:6.13.0.300'
+ // Huawei Analytics dependency to satisfy com.huawei.hianalytics.* references during R8
+ implementation 'com.huawei.hms:hianalytics:6.13.0.300'
+ // BouncyCastle provider required by Huawei secure utils used transitively by HMS
+ implementation 'org.bouncycastle:bcprov-jdk15to18:1.78.1'
+}
+
flutter {
source = "../.."
}
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
new file mode 100644
index 00000000..b657fa16
--- /dev/null
+++ b/android/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+-keep class com.huawei.** { *; }
+-dontwarn com.huawei.**
+
+-keep class org.bouncycastle.** { *; }
+-dontwarn org.bouncycastle.**
+
+-keep class io.flutter.** { *; }
+-dontwarn io.flutter.**
+
+-keep class kotlinx.coroutines.** { *; }
+-dontwarn kotlinx.coroutines.**
+
+-keep class **.R$* { *; }
+-keepclassmembers class ** { @androidx.annotation.Keep *; }
+-keep @androidx.annotation.Keep class * { *; }
+
+-keep public class * extends android.app.Application
+-keep public class * extends android.content.ContentProvider
+
+-keep class * implements android.os.Parcelable { *; }
+-keepclassmembers enum * { *; }
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 0d239126..a99f3deb 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -11,6 +11,16 @@
android:usesCleartextTraffic="true"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
+
+
+
+
+
+
+ when (call.method) {
+ "getHuaweiToken" -> {
+ CoroutineScope(Dispatchers.IO).launch {
+ try {
+ // Try to read appId from agconnect-services.json
+ var appId: String? = try {
+ AGConnectServicesConfig.fromContext(applicationContext)
+ .getString("client/app_id")
+ } catch (_: Exception) {
+ null
+ }
+
+ // Fallback to manifest meta-data if needed
+ if (appId.isNullOrBlank()) {
+ try {
+ val pm: PackageManager = applicationContext.packageManager
+ val ai: ApplicationInfo = pm.getApplicationInfo(applicationContext.packageName, PackageManager.GET_META_DATA)
+ val raw = ai.metaData?.getString("com.huawei.hms.client.appid")
+ if (!raw.isNullOrBlank()) {
+ appId = raw.removePrefix("appid=")
+ }
+ } catch (_: Exception) {
+ // ignore and handle below
+ }
+ }
+
+ if (appId.isNullOrBlank()) {
+ runOnUiThread {
+ result.error("HMS_TOKEN_ERROR", "Missing Huawei appId. Ensure agconnect-services.json or manifest meta-data is configured.", null)
+ }
+ return@launch
+ }
+ val token = HmsInstanceId.getInstance(applicationContext)
+ .getToken(appId, "HCM")
+ runOnUiThread {
+ result.success(token)
+ }
+ } catch (e: Exception) {
+ runOnUiThread {
+ result.error("HMS_TOKEN_ERROR", e.message, null)
+ }
+ }
+ }
+ }
+ else -> result.notImplemented()
+ }
+ }
+ }
}
diff --git a/android/build.gradle b/android/build.gradle
index 9db63328..03774aff 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,3 +1,16 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ maven { url 'https://developer.huawei.com/repo/' }
+ gradlePluginPortal()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:8.12.0'
+ classpath 'com.huawei.agconnect:agcp:1.9.1.301'
+ }
+}
+
allprojects {
repositories {
google()
@@ -14,6 +27,32 @@ subprojects {
project.evaluationDependsOn(":app")
}
+// Temporary safeguard: set missing namespaces for Android library modules based on their manifest package
+subprojects { Project subproject ->
+ subproject.pluginManager.withPlugin('com.android.library') {
+ def androidExt = subproject.extensions.findByName('android')
+ if (androidExt != null) {
+ def hasNamespaceProp = androidExt.hasProperty('namespace')
+ def currentNamespace = hasNamespaceProp ? androidExt.namespace : null
+ if (hasNamespaceProp && !currentNamespace) {
+ def manifestFile = file("${subproject.projectDir}/src/main/AndroidManifest.xml")
+ if (manifestFile.exists()) {
+ try {
+ def manifest = new groovy.xml.XmlSlurper().parse(manifestFile)
+ def manifestPackage = manifest.@package?.text()
+ if (manifestPackage) {
+ androidExt.namespace = manifestPackage
+ subproject.logger.lifecycle("[namespace-fix] Set namespace for ${subproject.path} to '${manifestPackage}' from AndroidManifest.xml")
+ }
+ } catch (Throwable t) {
+ subproject.logger.warn("[namespace-fix] Failed to read manifest for ${subproject.path}: ${t.message}")
+ }
+ }
+ }
+ }
+ }
+}
+
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html
index 4921e6e4..ce07f9e3 100644
--- a/android/build/reports/problems/problems-report.html
+++ b/android/build/reports/problems/problems-report.html
@@ -650,12 +650,12 @@ code + .copy-button {
diff --git a/android/settings.gradle b/android/settings.gradle
index e173b2c9..76846d0d 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -12,6 +12,7 @@ pluginManagement {
repositories {
google()
mavenCentral()
+ maven { url 'https://developer.huawei.com/repo/' }
gradlePluginPortal()
}
}
diff --git a/lib/core/firebase_api.dart b/lib/core/firebase_api.dart
index 2ba89290..2d89bd43 100644
--- a/lib/core/firebase_api.dart
+++ b/lib/core/firebase_api.dart
@@ -1,28 +1,27 @@
import 'dart:developer';
import 'dart:io';
+import 'package:eatvio_app/core/utils/toasts.dart';
+import 'package:flutter/services.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/token_storage.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
-import 'package:huawei_push/huawei_push.dart';
+import 'package:flutter/services.dart' show MethodChannel, PlatformException;
class FirebaseApi {
final _firebaseMessaging = FirebaseMessaging.instance;
Future getToken() async {
- await _firebaseMessaging.requestPermission();
+ String? token;
- String? token = await _firebaseMessaging.getToken();
-
- if (token == null && Platform.isAndroid) {
- final isHuawei = await _isHuaweiDevice();
- if (isHuawei) {
- token = await _getHuaweiPushToken();
- }
+ if (await _isHuaweiDevice()) {
+ token = await _getHuaweiPushTokenNative();
+ } else {
+ await _firebaseMessaging.requestPermission();
+ token = await _firebaseMessaging.getToken();
}
-
return token;
}
@@ -30,10 +29,11 @@ class FirebaseApi {
if (await TokenStorage.haveTokenInStorage()) {
try {
final deviceInfo = DeviceInfoPlugin();
+ final isHuawei = Platform.isAndroid ? await _isHuaweiDevice() : false;
await sl().post(
'/api/profile/notification/token/register',
data: {
- 'platform': Platform.isIOS ? 'ios' : 'android',
+ 'platform': Platform.isIOS ? 'ios' : (isHuawei ? 'huawei' : 'android'),
'hw_id': Platform.isIOS ? (await deviceInfo.iosInfo).identifierForVendor : (await deviceInfo.androidInfo).id,
'token': await getToken(),
},
@@ -47,33 +47,27 @@ class FirebaseApi {
Future _isHuaweiDevice() async {
if (!Platform.isAndroid) return false;
try {
- final android = await DeviceInfoPlugin().androidInfo;
- final brand = android.brand.toLowerCase();
- final manufacturer = android.manufacturer.toLowerCase();
- return brand.contains('huawei') || manufacturer.contains('huawei') || brand.contains('honor') || manufacturer.contains('honor');
+ final info = await DeviceInfoPlugin().androidInfo;
+ final brand = info.brand.toLowerCase();
+ final manufacturer = info.manufacturer.toLowerCase();
+ return brand.contains('huawei') || brand.contains('honor') || manufacturer.contains('huawei') || manufacturer.contains('honor');
} catch (_) {
return false;
}
}
- Future _getHuaweiPushToken() async {
+ Future _getHuaweiPushTokenNative() async {
try {
- await Push.turnOnPush();
-
- String? resolvedToken;
- final sub = Push.getTokenStream.listen((t) => resolvedToken ??= t, onError: (_) {});
-
- Push.getToken('');
-
- final started = DateTime.now();
- while (resolvedToken == null && DateTime.now().difference(started).inSeconds < 5) {
- await Future.delayed(const Duration(milliseconds: 100));
- }
-
- await sub.cancel();
- return resolvedToken;
+ const channel = MethodChannel('com.eatvioapp/hms_push');
+ final String? token = await channel.invokeMethod('getHuaweiToken');
+ return token;
+ } on PlatformException catch (e, st) {
+ showErrorToast('$e $st');
+ log(e.toString(), stackTrace: st);
+ return null;
} catch (e, st) {
- log('Huawei Push token error: $e', stackTrace: st);
+ showErrorToast('$e $st');
+ log(e.toString(), stackTrace: st);
return null;
}
}
diff --git a/pubspec.yaml b/pubspec.yaml
index a62a13af..98a4429f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -74,7 +74,6 @@ dependencies:
flutter_dotenv: ^5.2.1
uuid: ^4.5.1
appmetrica_plugin: ^3.3.0
- huawei_push: ^6.11.0+300
vk_id: ^1.0.3
flutter_login_yandex: ^0.0.5