Skip to content
This repository has been archived by the owner on Sep 17, 2024. It is now read-only.

Commit

Permalink
Merge pull request #156 from MXCzkEVM/dapp_hooks
Browse files Browse the repository at this point in the history
DApp hooks & WiFi hooks
  • Loading branch information
reasje authored Feb 2, 2024
2 parents b932af8 + 25afac8 commit 3577bf0
Show file tree
Hide file tree
Showing 31 changed files with 1,428 additions and 204 deletions.
4 changes: 3 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

<application tools:replace="android:label" android:label="AXS" android:name="${applicationName}" android:icon="@mipmap/ic_launcher">
<application tools:replace="android:label" android:label="AXS" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" android:theme="@style/LaunchTheme">


<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="axs_wallet_channel" />
Expand Down
2 changes: 1 addition & 1 deletion android/app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<style name="NormalTheme" parent="Theme.AppCompat">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
Expand Down
17 changes: 13 additions & 4 deletions assets/flutter_i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -325,15 +325,24 @@
"background_fetch_notice_title": "Optimize Your AXS Wallet Experience",
"background_fetch_notice_text": "To ensure peak performance of the AXS Wallet, we recommend keeping the app active in the background and excluding it from battery optimization settings. This helps maintain seamless functionality and uninterrupted access to your wallet.",
"acknowledge": "Acknowledge",
"background_notifications_service_launched_successfully": "Background notifications service launched successfully.",
"unable_to_launch_background_notification_service": "Unable to launch background notification service.",
"background_notifications_service_disabled_successfully": "Background notifications service disabled successfully.",
"low_balance": "Low balance",
"expected_transaction_fee": "Expected transaction fee",
"expected_epoch_occur": "Expected epoch occurrence",
"background_notifications": "Background notifications",
"occurrence": "Occurrence",
"frequency": "Frequency",
"experiencing_issues": "Experiencing issues?",
"background_service_solution": "Try re-enabling the app or check the battery optimization settings."
"background_service_solution": "Try re-enabling the app or check the battery optimization settings.",
"dapp_hooks": "DApp hooks",
"wifi_hooks": "Wi-Fi hooks",
"miner_hooks": "Miner hooks",
"location_permission_required_title": "Location permission required.",
"location_permission_required_text": "Oops, location permission is denied, It's essential for us to have location permission in order for Wi-Fi hooks to perform actively, Please grant location permission by going to settings.",
"open_settings": "Open settings",
"service_launched_successfully": "{0} service launched successfully.",
"unable_to_launch_service": "Unable to launch {0} service.",
"service_disabled_successfully": "{0} service disabled successfully.",
"location": "Location",
"location_service": "Location service",
"wifi_hooks_solutions": "Try re-enabling. \nRequirements :\n1. Location permission for always should be permitted \n2. Locations service should be running \n3. You should be connected to a Wi-Fi"
}
60 changes: 60 additions & 0 deletions ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,65 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# Start of the permission_handler configuration
target.build_configurations.each do |config|

# You can enable the permissions needed here. For example to enable camera
# permission, just remove the `#` character in front so it looks like this:
#
# ## dart: PermissionGroup.camera
# 'PERMISSION_CAMERA=1'
#
# Preprocessor definitions can be found at: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',

## dart: [PermissionGroup.calendarWriteOnly, PermissionGroup.calendar (iOS 16 and below)]
# 'PERMISSION_EVENTS=1',

## dart: [PermissionGroup.calendarFullAccess, PermissionGroup.calendar (iOS 17 and above)]
# 'PERMISSION_EVENTS_FULL_ACCESS=1',

## dart: PermissionGroup.reminders
# 'PERMISSION_REMINDERS=1',

## dart: PermissionGroup.contacts
'PERMISSION_CONTACTS=1',

## dart: PermissionGroup.camera
'PERMISSION_CAMERA=1',

## dart: PermissionGroup.microphone
'PERMISSION_MICROPHONE=1',

## dart: PermissionGroup.speech
# 'PERMISSION_SPEECH_RECOGNIZER=1',

## dart: PermissionGroup.photos
# 'PERMISSION_PHOTOS=1',

# dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
'PERMISSION_LOCATION=1',

# dart: PermissionGroup.notification
'PERMISSION_NOTIFICATIONS=1',

## dart: PermissionGroup.mediaLibrary
# 'PERMISSION_MEDIA_LIBRARY=1',

## dart: PermissionGroup.sensors
# 'PERMISSION_SENSORS=1',

## dart: PermissionGroup.bluetooth
# 'PERMISSION_BLUETOOTH=1',

## dart: PermissionGroup.appTrackingTransparency
# 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',

## dart: PermissionGroup.criticalAlerts
# 'PERMISSION_CRITICAL_ALERTS=1'
]

end
end
end
16 changes: 9 additions & 7 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.transistorsoft.fetch</string>
<string>com.mxc.axswallet.periodicalTasks</string>
<string>com.mxc.axswallet.dappHooksTasks</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
Expand Down Expand Up @@ -91,7 +97,7 @@
<key>NSCalendarsUsageDescription</key>
<string>Would you allow AXS Wallet to use the Calendar?</string>
<key>NSCameraUsageDescription</key>
<string>AXS Wallet needs camera access to scan QR codes for transactions. Scanned data is stored locally and isn&apos;t shared.</string>
<string>AXS Wallet needs camera access to scan QR codes for transactions. Scanned data is stored locally and isn't shared.</string>
<key>NSContactsUsageDescription</key>
<string>Would you allow AXS Wallet to use the Contacts?</string>
<key>NSFaceIDUsageDescription</key>
Expand All @@ -105,7 +111,7 @@
<key>NSLocationWhenInUseUsageDescription</key>
<string>Would you allow AXS Wallet to use the Location?</string>
<key>NSMicrophoneUsageDescription</key>
<string>AXS Wallet needs microphone access to scan QR codes for transactions. Scanned data is stored locally and isn&apos;t shared.</string>
<string>AXS Wallet needs microphone access to scan QR codes for transactions. Scanned data is stored locally and isn't shared.</string>
<key>NSMotionUsageDescription</key>
<string>Would you allow AXS Wallet to use the Motion?</string>
<key>NSPhotoLibraryAddUsageDescription</key>
Expand All @@ -121,6 +127,7 @@
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>location</string>
<string>processing</string>
<string>remote-notification</string>
</array>
Expand All @@ -141,11 +148,6 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.transistorsoft.fetch</string>
<string>com.mxc.axswallet.periodicalTasks</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
Expand Down
32 changes: 32 additions & 0 deletions lib/common/utils/permission.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ class PermissionUtils {
].request();
}

static Future<void> requestLocationPermission() async {
await Permission.locationWhenInUse.request();
await Permission.locationAlways.request();
}

/// Request the notification permission and return the detailed status.
static Future<AuthorizationStatus> requestNotificationPermission() async {
FirebaseMessaging messaging = FirebaseMessaging.instance;
Expand All @@ -69,6 +74,12 @@ class PermissionUtils {
}

/// Return true if the permission is granted (The permission might have some limitation also, In this case It's true also).
static Future<bool> checkLocationPermission() async {
PermissionStatus status = await getLocationPermission();

return status == PermissionStatus.granted;
}

static Future<bool> checkNotificationPermission() async {
AuthorizationStatus authorizationStatus = await getNotificationPermission();

Expand All @@ -82,13 +93,34 @@ class PermissionUtils {
authorizationStatus == AuthorizationStatus.provisional;
}

static Future<PermissionStatus> getLocationPermission() async {
PermissionStatus locationStatus = await Permission.location.status;

return locationStatus;
}

static Future<AuthorizationStatus> getNotificationPermission() async {
NotificationSettings settings =
await FirebaseMessaging.instance.getNotificationSettings();

return settings.authorizationStatus;
}

static Future<bool> initLocationPermission() async {
PermissionStatus status = await getLocationPermission();
if (status == PermissionStatus.granted) {
return true;
}

if (status == PermissionStatus.permanentlyDenied) {
return false;
}
// permission not granted or the status is not determined
await requestLocationPermission();
final isGranted = await checkLocationPermission();
return isGranted;
}

static Future<bool> initNotificationPermission() async {
bool isGranted = await checkNotificationPermission();
if (isGranted) {
Expand Down
1 change: 1 addition & 0 deletions lib/core/core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export 'src/disposable.dart';
export 'src/cache/base_cache.dart';
export 'src/notification.dart';
export 'src/firebase/firebase.dart';
export 'src/background_process/background_process.dart';
2 changes: 2 additions & 0 deletions lib/core/src/background_process/background_process.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'dapp_hooks_service.dart';
export 'notifications_service.dart';
66 changes: 66 additions & 0 deletions lib/core/src/background_process/dapp_hooks_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'package:background_fetch/background_fetch.dart';
import 'package:datadashwallet/core/core.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:mxc_logic/mxc_logic.dart';

class DAppHooksService {
@pragma(
'vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
static void dappHooksServiceCallBackDispatcher(HeadlessTask task) async {
String taskId = task.taskId;
bool isTimeout = task.timeout;
if (isTimeout) {
// This task has exceeded its allowed running-time.
// You must stop what you're doing and immediately .finish(taskId)
print("[BackgroundFetch] Headless task timed-out: $taskId");
BackgroundFetch.finish(taskId);
return;
}
dappHooksServiceCallBackDispatcherForeground(taskId);
}

static void dappHooksServiceCallBackDispatcherForeground(
String taskId) async {
try {
await loadProviders();

final container = ProviderContainer();
final authUseCase = container.read(authUseCaseProvider);
final chainConfigurationUseCase =
container.read(chainConfigurationUseCaseProvider);
final accountUseCase = container.read(accountUseCaseProvider);
// final backgroundFetchConfigUseCase =
// container.read(backgroundFetchConfigUseCaseProvider);
final dAppHooksUseCase = container.read(dAppHooksUseCaseProvider);

final selectedNetwork =
chainConfigurationUseCase.getCurrentNetworkWithoutRefresh();
DAppHooksModel dappHooksData = dAppHooksUseCase.dappHooksData.value;
final chainId = selectedNetwork.chainId;

final isLoggedIn = authUseCase.loggedIn;
final account = accountUseCase.account.value;
final serviceEnabled = dappHooksData.enabled;
final wifiHooksEnabled = dappHooksData.wifiHooks.enabled;

// Make sure user is logged in
if (isLoggedIn && Config.isMxcChains(chainId) && serviceEnabled) {
AXSNotification().setupFlutterNotifications(shouldInitFirebase: false);

if (wifiHooksEnabled) {
await dAppHooksUseCase.sendWifiInfo(
account!,
);
}

dAppHooksUseCase.updateItem(dappHooksData);
BackgroundFetch.finish(taskId);
} else {
// terminate background fetch
BackgroundFetch.stop(taskId);
}
} catch (e) {
BackgroundFetch.finish(taskId);
}
}
}
88 changes: 88 additions & 0 deletions lib/core/src/background_process/notifications_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import 'package:background_fetch/background_fetch.dart';
import 'package:datadashwallet/core/core.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:mxc_logic/mxc_logic.dart';

class NotificationsService {
@pragma(
'vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
static void callbackDispatcher(HeadlessTask task) async {
String taskId = task.taskId;
bool isTimeout = task.timeout;
if (isTimeout) {
// This task has exceeded its allowed running-time.
// You must stop what you're doing and immediately .finish(taskId)
print("[BackgroundFetch] Headless task timed-out: $taskId");
BackgroundFetch.finish(taskId);
return;
}
callbackDispatcherForeGround(taskId);
}

// Foreground
static void callbackDispatcherForeGround(String taskId) async {
try {
await loadProviders();

final container = ProviderContainer();
final authUseCase = container.read(authUseCaseProvider);
final chainConfigurationUseCase =
container.read(chainConfigurationUseCaseProvider);
final accountUseCase = container.read(accountUseCaseProvider);
final backgroundFetchConfigUseCase =
container.read(backgroundFetchConfigUseCaseProvider);

final selectedNetwork =
chainConfigurationUseCase.getCurrentNetworkWithoutRefresh();
PeriodicalCallData periodicalCallData =
backgroundFetchConfigUseCase.periodicalCallData.value;
final chainId = selectedNetwork.chainId;

final isLoggedIn = authUseCase.loggedIn;
final account = accountUseCase.account.value;
final lowBalanceLimit = periodicalCallData.lowBalanceLimit;
final expectedTransactionFee = periodicalCallData.expectedTransactionFee;
final lowBalanceLimitEnabled = periodicalCallData.lowBalanceLimitEnabled;
final expectedTransactionFeeEnabled =
periodicalCallData.expectedTransactionFeeEnabled;
final lastEpoch = periodicalCallData.lasEpoch;
final expectedEpochOccurrence =
periodicalCallData.expectedEpochOccurrence;
final expectedEpochOccurrenceEnabled =
periodicalCallData.expectedEpochOccurrenceEnabled;
final serviceEnabled = periodicalCallData.serviceEnabled;

// Make sure user is logged in
if (isLoggedIn && Config.isMxcChains(chainId) && serviceEnabled) {
AXSNotification().setupFlutterNotifications(shouldInitFirebase: false);

if (lowBalanceLimitEnabled) {
await backgroundFetchConfigUseCase.checkLowBalance(
account!, lowBalanceLimit);
}

if (expectedTransactionFeeEnabled) {
await backgroundFetchConfigUseCase
.checkTransactionFee(expectedTransactionFee);
}

if (expectedEpochOccurrenceEnabled) {
periodicalCallData =
await backgroundFetchConfigUseCase.checkEpochOccur(
periodicalCallData,
lastEpoch,
expectedEpochOccurrence,
chainId);
}

backgroundFetchConfigUseCase.updateItem(periodicalCallData);
BackgroundFetch.finish(taskId);
} else {
// terminate background fetch
BackgroundFetch.stop(taskId);
}
} catch (e) {
BackgroundFetch.finish(taskId);
}
}
}
Loading

0 comments on commit 3577bf0

Please sign in to comment.