├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build-example.sh │ ├── install-flutter.sh │ ├── install-tools.sh │ └── main.yml ├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── libraries │ ├── Dart_SDK.xml │ └── Flutter_Plugins.xml ├── misc.xml ├── modules.xml ├── runConfigurations │ └── example_lib_main_dart.xml ├── vcs.xml └── workspace.xml ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── .idea │ ├── .gitignore │ ├── .name │ ├── compiler.xml │ ├── gradle.xml │ ├── migrations.xml │ ├── misc.xml │ └── vcs.xml ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── lucdotdev │ │ └── hover_ussd │ │ ├── HoverUssdApi.java │ │ ├── HoverUssdObjectToMap.java │ │ └── HoverUssdPlugin.java │ └── test │ └── java │ └── com │ └── lucdotdev │ └── hover_ussd │ └── HoverUssdPluginTest.java ├── docs └── hover.png ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── io │ │ │ │ │ └── flutter │ │ │ │ │ └── app │ │ │ │ │ └── FlutterMultiDexApplication.java │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── integration_test │ └── plugin_integration_test.dart ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ └── RunnerTests │ │ └── RunnerTests.swift ├── lib │ └── main.dart ├── pubspec.lock ├── pubspec.yaml └── test │ └── widget_test.dart ├── hover_ussd.iml ├── lib ├── hover_ussd.dart ├── hover_ussd_plugin.dart └── models │ ├── download_action_state.dart │ ├── hover_action.dart │ ├── transaction.dart │ └── transaction_state.dart ├── pubspec.lock ├── pubspec.yaml └── test └── hover_ussd_test.dart /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/build-example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ACTION=$1 4 | 5 | if [ "$ACTION" == "android" ] 6 | then 7 | cd example-- \ 8 | flutter build apk --debug 9 | exit 10 | fi 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/install-flutter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BRANCH=$1 4 | 5 | git clone https://github.com/flutter/flutter.git --depth 1 -b $BRANCH _flutter 6 | echo "::add-path::$GITHUB_WORKSPACE/_flutter/bin" 7 | -------------------------------------------------------------------------------- /.github/workflows/install-tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | flutter pub global activate melos 4 | echo "::add-path::$HOME/.pub-cache/bin" 5 | echo "::add-path::$GITHUB_WORKSPACE/_flutter/.pub-cache/bin" 6 | echo "::add-path::$GITHUB_WORKSPACE/_flutter/bin/cache/dart-sdk/bin" 7 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | # This workflow is triggered on pushes to the repository. 4 | 5 | on: 6 | push: 7 | 8 | # on: push # Default will running for every branch. 9 | 10 | jobs: 11 | build: 12 | # This job will run on ubuntu virtual machine 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | # Setup Java environment in order to build the Android app. 17 | - uses: actions/checkout@v2 18 | - uses: actions/setup-java@v1 19 | with: 20 | java-version: '12.x' 21 | 22 | # Setup the flutter environment. 23 | - uses: subosito/flutter-action@v1 24 | with: 25 | channel: 'stable' # 'dev', 'alpha', default to: 'stable' 26 | # flutter-version: '1.12.x' # you can also specify exact version of flutter 27 | - run : cd example 28 | # Get flutter dependencies. 29 | - run: flutter pub get 30 | 31 | # Build apk. 32 | - run: flutter build apk 33 | 34 | # Upload generated apk to the artifacts. 35 | - uses: actions/upload-artifact@v1 36 | with: 37 | name: release-apk 38 | path: build/app/outputs/apk/release/app-release.apk 39 | 40 | 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/example_lib_main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 18 | 19 | 20 | 26 | 27 | 28 | 33 | 36 | 37 | 39 | 40 | 42 | { 43 | "customColor": "", 44 | "associatedIndex": 0 45 | } 46 | 47 | 48 | 51 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 1596904412123 80 | 84 | 85 | 92 | 95 | 96 | 97 | 99 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: bf9f3a3dcfea3022f9cf2dfc3ab10b120b48b19d 8 | channel: master 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.1.0 2 | * rethink entire plugin 3 | 4 | ## 2.0.0 5 | * revert to hover standart 6 | * change sendUssd() to startTransaction() 7 | ## 1.0.0a 8 | * change from hover standard to hover noSms 9 | * You can now customize the hover the by passing branding or drawable to the HoverUssd Contruction 10 | * You can also provide the theme of hover ussd 11 | * 12 | ## 0.0.2+2 13 | * update readme 14 | ## 0.0.2+1 15 | * improve code 16 | ## 0.0.2 17 | * improve performance 18 | * update readme 19 | ## 0.0.1 20 | * Initial release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Luc Mwansa 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hover_ussd 2 | 3 | ![build](https://github.com/lucdotdev/hover_ussd/workflows/build/badge.svg) 4 | [![Pub](https://img.shields.io/pub/v/hover_ussd)](https://pub.dartlang.org/packages/hover_ussd) 5 | [![Star on GitHub](https://img.shields.io/github/stars/lucdotdev/hover_ussd)](https://github.com/lucdotdev/hover_ussd) 6 | [![Flutter Website](https://img.shields.io/badge/flutter-website-deepskyblue.svg)](https://flutter.dev/docs/development/data-and-backend/state-mgmt/options#bloc--rx) 7 | [![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) 8 | 9 | 10 | 11 | © image by Francis Mwakitumbula 12 | 13 | 14 | A flutter plugin implemanting usehover.com ussd gateway sdk using Android Intent and receiving the transaction information back in response. 15 | **android only** 16 | 17 | ## Getting Started 18 | 19 | 20 | ## Hover USSD Plugin 21 | 22 | The Hover USSD plugin provides a simple and easy-to-use interface for integrating Hover USSD services into your Flutter applications. It allows you to start USSD transactions, listen to transaction states, and retrieve available actions from the Hover API. 23 | 24 | ## Usage 25 | 26 | 1. **Installation** 27 | 28 | Add `hover_ussd` to your `pubspec.yaml` file: 29 | 30 | ```yaml 31 | dependencies: 32 | hover_ussd: ^latest_version 33 | ``` 34 | 35 | Then, run: 36 | 37 | ```sh 38 | flutter pub get 39 | ``` 40 | 41 | 2. **Initialization** 42 | 43 | Initialize HoverUssd with your Hover API key at [docs.usehover.com](https://docs.usehover.com/), branding, and logo information: 44 | 45 | ```dart 46 | final HoverUssd _hoverUssd = HoverUssd(); 47 | await _hoverUssd.initialize( 48 | apiKey: "YOUR_API_KEY", 49 | branding: "Your App Name", 50 | logo: "ic_launcher", 51 | notificationLogo: "ic_launcher", 52 | ); 53 | ``` 54 | 55 | 3. **Start a Transaction** 56 | 57 | Start a USSD transaction with the provided parameters: 58 | 59 | ```dart 60 | await _hoverUssd.startTransaction( 61 | actionId: "ACTION_ID", 62 | extras: {}, 63 | theme: "myhoverTheme", //located in android/app/main/values/styles.xml 64 | header: "Hover Ussd Example", 65 | showUserStepDescriptions: true, 66 | ); 67 | ``` 68 | 69 | 4. **Permissions Check** 70 | 71 | Check if the app has all the necessary permissions: 72 | 73 | ```dart 74 | bool hasPermissions = await _hoverUssd.hasAllPermissions(); 75 | ``` 76 | 77 | 5. **Overlay and Accessibility Check** 78 | 79 | Check if the app has overlay and accessibility permissions: 80 | 81 | ```dart 82 | bool isOverlayEnabled = await _hoverUssd.isOverlayEnabled(); 83 | bool isAccessibilityEnabled = await _hoverUssd.isAccessibilityEnabled(); 84 | ``` 85 | 86 | 6. **Retrieve Actions** 87 | 88 | Retrieve a list of available actions: 89 | 90 | ```dart 91 | List actions = await _hoverUssd.getAllActions(); 92 | ``` 93 | 94 | 7. **Refresh Actions** 95 | 96 | Refresh actions to get the latest updates: 97 | 98 | ```dart 99 | await _hoverUssd.refreshActions(); 100 | ``` 101 | 102 | 8. **Listen to Transaction States** 103 | 104 | Get a stream of transaction states: 105 | 106 | ```dart 107 | Stream transactionStream = _hoverUssd.getUssdTransactionState(); 108 | ``` 109 | 110 | 9. **Listen to Download Action States** 111 | 112 | Get a stream of download action states: 113 | 114 | ```dart 115 | Stream downloadStream = _hoverUssd.getDownloadActionState(); 116 | ``` 117 | 118 | ## Customization 119 | 120 | 121 | * ### To use your own theme style 122 | 123 | #### add your style to android/app/main/values/styles.xml 124 | ```xml 125 | 126 | 127 | 128 | 133 | 139 | 142 | 156 | 157 | 164 | 165 | 171 | 172 | 173 | 174 | ``` 175 | 176 | * ### Customize an hover session 177 | ```dart 178 | await _hoverUssd.startTransaction( 179 | actionId: "ACTION_ID", 180 | extras: {}, 181 | theme: "myhoverTheme", // as in android/app/main/values/styles.xml 182 | header: "Hover Ussd Example"// the title of your hover session, 183 | showUserStepDescriptions: true, 184 | ); 185 | ``` 186 | 187 | * ### Add a custom permissions activity 188 | 189 | ```dart 190 | 191 | final String activityName = "com.example.yourApp.custompermissionsActivity"; 192 | _hoverUssd.setPermissionsActivity(activityName: activityName); 193 | 194 | 195 | ``` 196 | 197 | ### Example 198 | 199 | ```dart 200 | class MyHomePage extends StatefulWidget { 201 | const MyHomePage({super.key}); 202 | 203 | @override 204 | State createState() => _MyHomePageState(); 205 | } 206 | 207 | class _MyHomePageState extends State { 208 | late final HoverUssd _hoverUssd = HoverUssd(); 209 | // Create an instance of HoverUssd you can pass it to a provider, 210 | // or use get_it to make it available to the entire app 211 | 212 | late StreamSubscription _transactionListening; 213 | late StreamSubscription _actionDownloadListening; 214 | bool _hasPermissions = false; 215 | bool _isOverlayEnabled = false; 216 | bool _isAccessibilityEnabled = false; 217 | 218 | @override 219 | void initState() { 220 | _transactionListening = 221 | _hoverUssd.getUssdTransactionState().listen((event) { 222 | ScaffoldMessenger.of(context) 223 | .showSnackBar(SnackBar(content: Text(event.toMap().toString()))); 224 | }); 225 | 226 | _actionDownloadListening = 227 | _hoverUssd.getDownloadActionState().listen((event) { 228 | ScaffoldMessenger.of(context) 229 | .showSnackBar(SnackBar(content: Text(event.toString()))); 230 | }); 231 | _checkAccessibility(); 232 | _checkPermissions(); 233 | _checkOverlay(); 234 | 235 | //initialize hover after the downlad listener is set 236 | 237 | _actionDownloadListening.onData((event) { 238 | _initHover(); 239 | }); 240 | 241 | super.initState(); 242 | } 243 | 244 | @override 245 | void dispose() { 246 | _transactionListening.cancel(); 247 | _actionDownloadListening.cancel(); 248 | super.dispose(); 249 | } 250 | //check for permissions 251 | 252 | Future _checkPermissions() async { 253 | _hasPermissions = await _hoverUssd.hasAllPermissions(); 254 | setState(() {}); 255 | } 256 | 257 | Future _checkAccessibility() async { 258 | _isAccessibilityEnabled = await _hoverUssd.isAccessibilityEnabled(); 259 | setState(() {}); 260 | } 261 | 262 | Future _checkOverlay() async { 263 | _isOverlayEnabled = await _hoverUssd.isOverlayEnabled(); 264 | setState(() {}); 265 | } 266 | 267 | Future _initHover() async { 268 | try { 269 | await _hoverUssd.initialize( 270 | apiKey: "15ccc2bd81801d8c5fbfd5847d3b4e77", 271 | branding: "My Hover App", 272 | logo: "ic_launcher", 273 | notificationLogo: "ic_launcher", 274 | ); 275 | } catch (e) { 276 | print(e); 277 | } 278 | } 279 | 280 | Future _getAndGoToActions() async { 281 | final actions = await _hoverUssd.getAllActions(); 282 | Navigator.push( 283 | context, 284 | MaterialPageRoute( 285 | builder: (context) => HoverActionListPage( 286 | hoverActions: actions, 287 | ), 288 | ), 289 | ); 290 | } 291 | 292 | @override 293 | Widget build(BuildContext context) { 294 | return Scaffold( 295 | appBar: AppBar( 296 | title: const Text('Hover Ussd Example'), 297 | ), 298 | body: Center( 299 | child: Column( 300 | mainAxisAlignment: MainAxisAlignment.center, 301 | crossAxisAlignment: CrossAxisAlignment.center, 302 | children: [ 303 | ElevatedButton( 304 | onPressed: () { 305 | _hoverUssd.startTransaction( 306 | actionId: "c6e45e62", 307 | extras: {"price": "4000"}, 308 | theme: "HoverTheme", 309 | header: "Hover Ussd Example", 310 | showUserStepDescriptions: true, 311 | ); 312 | }, 313 | child: const Text("Start Transaction"), 314 | ), 315 | ElevatedButton( 316 | onPressed: _getAndGoToActions, 317 | child: const Text("Get Actions"), 318 | ), 319 | 320 | //refresh actions 321 | ElevatedButton( 322 | onPressed: () { 323 | _hoverUssd.refreshActions(); 324 | }, 325 | child: const Text("Refresh Actions"), 326 | ), 327 | Text( 328 | _hasPermissions 329 | ? "Permissions Granted" 330 | : "Permissions Not Granted", 331 | style: TextStyle( 332 | color: _hasPermissions ? Colors.green : Colors.red, 333 | fontWeight: FontWeight.bold, 334 | ), 335 | ), 336 | Text( 337 | _isAccessibilityEnabled 338 | ? "Accessibility Granted" 339 | : "Accessibility Not Granted", 340 | style: TextStyle( 341 | color: _isAccessibilityEnabled ? Colors.green : Colors.red, 342 | fontWeight: FontWeight.bold, 343 | ), 344 | ), 345 | Text( 346 | _isOverlayEnabled ? "Overlay Granted" : "Overlay Not Granted", 347 | style: TextStyle( 348 | color: _isOverlayEnabled ? Colors.green : Colors.red, 349 | fontWeight: FontWeight.bold, 350 | ), 351 | ), 352 | 353 | StreamBuilder( 354 | stream: _hoverUssd.getDownloadActionState(), 355 | builder: (context, snapshot) { 356 | final state = snapshot.data; 357 | 358 | String statusText; 359 | Color textColor; 360 | 361 | if (state is ActionDownloaded) { 362 | statusText = "Actions Downloaded"; 363 | textColor = Colors.green; 364 | } else if (state is ActionDownloadFailed) { 365 | statusText = "Actions Not Downloaded"; 366 | textColor = Colors.red; 367 | } else if (state is ActionDownloading) { 368 | statusText = "Actions Downloading"; 369 | textColor = 370 | Colors.blue; // Adjust color as per your preference 371 | } else { 372 | statusText = "Action Download Status: Unknown"; 373 | textColor = Colors.grey; 374 | } 375 | 376 | return Text( 377 | statusText, 378 | style: 379 | TextStyle(color: textColor, fontWeight: FontWeight.bold), 380 | ); 381 | }, 382 | ), 383 | StreamBuilder( 384 | stream: _hoverUssd.getUssdTransactionState(), 385 | builder: (BuildContext context, snapshot) { 386 | if (snapshot.data is SmsParsed) { 387 | return Text("Sms parsed : \n${snapshot.data!.toMap()}"); 388 | } 389 | if (snapshot.data is UssdSucceeded) { 390 | return Text("Ussd Succeded : \n${snapshot.data!.toMap()}"); 391 | } 392 | if (snapshot.data is UssdLoading) { 393 | return const Text("loading..."); 394 | } 395 | if (snapshot.data is UssdFailed) { 396 | return Text("Ussd Failed : \n${snapshot.data!.toMap()}"); 397 | } 398 | if (snapshot.data is EmptyState) { 399 | return const Text("Empty State"); 400 | } 401 | return const Text("No state"); 402 | }, 403 | ), 404 | ], 405 | ), 406 | ), 407 | ); 408 | } 409 | } 410 | 411 | 412 | ``` 413 | 414 | 415 | ## Important 416 | * **This is a unofficial plugin** 417 | ## Credit 418 | * Thanks to the authors of useHover android sdk, this work based of it 419 | 420 | ## Maintainers 421 | - [lucdotdev](mailto:lucdotdev@gmail.com) -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .cxx 10 | 11 | /tempsLibs 12 | -------------------------------------------------------------------------------- /android/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /android/.idea/.name: -------------------------------------------------------------------------------- 1 | hover_ussd -------------------------------------------------------------------------------- /android/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | -------------------------------------------------------------------------------- /android/.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /android/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.lucdotdev.hover_ussd' 2 | version '2.1.0' 3 | 4 | buildscript { 5 | repositories { 6 | google() 7 | mavenCentral() 8 | maven { url "https://maven.usehover.com/snapshots" } 9 | 10 | } 11 | 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:7.3.0' 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | maven { url "https://maven.usehover.com/snapshots" } 22 | 23 | } 24 | } 25 | 26 | apply plugin: 'com.android.library' 27 | 28 | android { 29 | if (project.android.hasProperty("namespace")) { 30 | namespace 'com.lucdotdev.hover_ussd' 31 | } 32 | 33 | compileSdkVersion 33 34 | 35 | compileOptions { 36 | sourceCompatibility JavaVersion.VERSION_1_8 37 | targetCompatibility JavaVersion.VERSION_1_8 38 | } 39 | 40 | defaultConfig { 41 | minSdkVersion 19 42 | } 43 | 44 | dependencies { 45 | testImplementation 'junit:junit:4.13.2' 46 | testImplementation 'org.mockito:mockito-core:5.0.0' 47 | implementation 'com.hover:android-sdk:2.0.0-beta04-noSMS' 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'hover_ussd' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java: -------------------------------------------------------------------------------- 1 | package com.lucdotdev.hover_ussd; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.os.Build; 9 | 10 | import androidx.core.content.ContextCompat; 11 | 12 | import com.hover.sdk.actions.HoverAction; 13 | import com.hover.sdk.api.Hover; 14 | import com.hover.sdk.api.HoverParameters; 15 | import com.hover.sdk.database.HoverRoomDatabase; 16 | import com.hover.sdk.transactions.Transaction; 17 | 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | public class HoverUssdApi { 24 | 25 | private final Activity activity; 26 | private final Context context; 27 | 28 | public HoverUssdApi(Activity activity, Context context) { 29 | this.activity = activity; 30 | this.context = context; 31 | } 32 | 33 | public void initialize(String apiKey, String branding, String logo, String notificationLogo, Hover.DownloadListener downloadListener) { 34 | Hover.initialize(context, apiKey, false, downloadListener); 35 | 36 | int logoResourceId = getResourceId(logo == null ? "ic_launcher" : logo); 37 | int notificationLogoResourceId = getResourceId(notificationLogo == null ? "ic_launcher" : notificationLogo); 38 | Hover.setBranding(branding == null ? "Hover Ussd Plugin" : branding, logoResourceId, notificationLogoResourceId, context); 39 | } 40 | 41 | public boolean hasAllPerms() { 42 | return Hover.hasAllPerms(context); 43 | } 44 | 45 | public boolean hasAccessibilityPermission() { 46 | return Hover.isAccessibilityEnabled(context); 47 | } 48 | 49 | public boolean hasOverlayPermission() { 50 | return Hover.isOverlayEnabled(context); 51 | } 52 | 53 | public boolean hasContactPermission() { 54 | return Build.VERSION.SDK_INT < 23 || hasPermission(new String[]{Manifest.permission.READ_CONTACTS}, context); 55 | } 56 | 57 | public boolean hasWritePermission() { 58 | return Build.VERSION.SDK_INT < 23 || hasPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, context); 59 | } 60 | 61 | public boolean hasSmsPermission() { 62 | return Build.VERSION.SDK_INT < 23 || hasPermission(new String[]{Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS}, context); 63 | } 64 | 65 | public boolean hasPhonePermission() { 66 | return Build.VERSION.SDK_INT < 23 || hasPermission(new String[]{Manifest.permission.READ_PHONE_STATE}, context); 67 | } 68 | 69 | private static boolean hasPermission(String[] permissions, Context context) { 70 | if (context == null) return false; 71 | for (String permission : permissions) { 72 | if (ContextCompat.checkSelfPermission(context, permission) != android.content.pm.PackageManager.PERMISSION_GRANTED) { 73 | return false; 74 | } 75 | } 76 | return true; 77 | } 78 | 79 | public ArrayList> getAllActions() { 80 | List actions = HoverRoomDatabase.getInstance(context).actionDao().getAll(); 81 | ArrayList> mapActions = new ArrayList<>(); 82 | 83 | for (HoverAction action : actions) { 84 | Map mapAction = HoverUssdObjectToMap.convertHoverActionToMap(action); 85 | mapActions.add(mapAction); 86 | } 87 | 88 | return mapActions; 89 | } 90 | 91 | public void refreshActions(Hover.DownloadListener actionDownloadListener) { 92 | Hover.updateActionConfigs(actionDownloadListener, context); 93 | } 94 | 95 | public ArrayList> getAllTransaction() { 96 | List transactions = HoverRoomDatabase.getInstance(context).transactionDao().getAll(); 97 | ArrayList> mapTransactions = new ArrayList<>(); 98 | 99 | for (Transaction transaction : transactions) { 100 | Map mapTransaction = HoverUssdObjectToMap.convertTransactionToMap(transaction); 101 | mapTransactions.add(mapTransaction); 102 | } 103 | 104 | return mapTransactions; 105 | } 106 | 107 | private int getResourceId(String resourceName) { 108 | try { 109 | return context.getResources().getIdentifier(resourceName, "mipmap", activity.getPackageName()); 110 | } catch (Exception e) { 111 | e.printStackTrace(); 112 | return 0; 113 | } 114 | } 115 | public void setPermissionsActivity(String activityString){ 116 | Hover.setPermissionActivity(activityString, context); 117 | } 118 | 119 | public void sendUssd(String action_id, 120 | HashMap extra, 121 | String theme, 122 | String header, 123 | String initialProcessingMessage, 124 | boolean showUserStepDescriptions, 125 | int finalMsgDisplayTime, 126 | BroadcastReceiver transactionStateReceiver) { 127 | 128 | final HoverParameters.Builder builder = new HoverParameters.Builder(context); 129 | 130 | builder.request(action_id); 131 | 132 | if (extra != null) { 133 | if (!extra.isEmpty()) { 134 | for (Map.Entry entry : extra.entrySet()) { 135 | builder.extra(entry.getKey(), entry.getValue()); 136 | } 137 | } 138 | } 139 | 140 | if (theme != null) { 141 | int id = context.getResources().getIdentifier(theme, "style", context.getPackageName()); 142 | builder.style(id); 143 | } 144 | if (header != null) { 145 | builder.setHeader(header); 146 | } 147 | if (initialProcessingMessage != null) { 148 | builder.initialProcessingMessage(initialProcessingMessage); 149 | } 150 | 151 | builder.showUserStepDescriptions(showUserStepDescriptions); 152 | 153 | if (finalMsgDisplayTime != 0) { 154 | builder.finalMsgDisplayTime(finalMsgDisplayTime); 155 | } 156 | 157 | Intent buildIntent = builder.buildIntent(); 158 | activity.startActivityForResult(buildIntent, 0); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdObjectToMap.java: -------------------------------------------------------------------------------- 1 | package com.lucdotdev.hover_ussd; 2 | 3 | import com.hover.sdk.actions.HoverAction; 4 | import com.hover.sdk.transactions.Transaction; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class HoverUssdObjectToMap { 10 | 11 | public static Map convertHoverActionToMap(HoverAction hoverAction) { 12 | Map map = new HashMap<>(); 13 | map.put("id", hoverAction.id); 14 | map.put("public_id", hoverAction.public_id); 15 | map.put("name", hoverAction.name); 16 | map.put("channel_id", hoverAction.channel_id); 17 | map.put("network_name", hoverAction.network_name); 18 | map.put("country_alpha2", hoverAction.country_alpha2); 19 | map.put("root_code", hoverAction.root_code); 20 | map.put("transport_type", hoverAction.transport_type); 21 | map.put("transaction_type", hoverAction.transaction_type); 22 | map.put("from_institution_id", hoverAction.from_institution_id); 23 | map.put("from_institution_name", hoverAction.from_institution_name); 24 | map.put("from_institution_logo", hoverAction.from_institution_logo); 25 | map.put("to_institution_id", hoverAction.to_institution_id); 26 | map.put("to_institution_name", hoverAction.to_institution_name); 27 | map.put("to_institution_logo", hoverAction.to_institution_logo); 28 | map.put("to_country_alpha2", hoverAction.to_country_alpha2); 29 | map.put("created_timestamp", hoverAction.created_timestamp); 30 | map.put("updated_timestamp", hoverAction.updated_timestamp); 31 | map.put("bounty_amount", hoverAction.bounty_amount); 32 | map.put("bounty_is_open", hoverAction.bounty_is_open); 33 | map.put("is_ready", hoverAction.is_ready); 34 | map.put("bonus_percent", hoverAction.bonus_percent); 35 | map.put("bonus_message", hoverAction.bonus_message); 36 | 37 | // Ignoring 'parsers' field as it's not included in the map 38 | 39 | return map; 40 | } 41 | 42 | public static Map convertTransactionToMap(Transaction transaction) { 43 | Map map = new HashMap<>(); 44 | 45 | map.put("id", transaction.id); 46 | map.put("channelId", transaction.channelId); 47 | map.put("statusId", transaction.statusId); 48 | map.put("env", transaction.env); 49 | map.put("actionId", transaction.actionId); 50 | map.put("actionName", transaction.actionName); 51 | map.put("uuid", transaction.uuid); 52 | map.put("status", transaction.status); 53 | map.put("category", transaction.category); 54 | map.put("userMessage", transaction.userMessage); 55 | map.put("networkHni", transaction.networkHni); 56 | map.put("myType", transaction.myType); 57 | map.put("toInstitutionName", transaction.toInstitutionName); 58 | map.put("fromInstitutionName", transaction.fromInstitutionName); 59 | map.put("configVersion", transaction.configVersion); 60 | map.put("sdkVersion", transaction.sdkVersion); 61 | map.put("reqTimestamp", transaction.reqTimestamp); 62 | map.put("updatedTimestamp", transaction.updatedTimestamp); 63 | 64 | 65 | 66 | return map; 67 | } 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java: -------------------------------------------------------------------------------- 1 | package com.lucdotdev.hover_ussd; 2 | 3 | 4 | import android.app.Activity; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.IntentFilter; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 12 | 13 | import com.hover.sdk.actions.HoverAction; 14 | import com.hover.sdk.api.Hover; 15 | 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | import java.util.Objects; 20 | 21 | import io.flutter.Log; 22 | import io.flutter.embedding.engine.plugins.FlutterPlugin; 23 | import io.flutter.embedding.engine.plugins.activity.ActivityAware; 24 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; 25 | import io.flutter.plugin.common.EventChannel; 26 | import io.flutter.plugin.common.MethodCall; 27 | import io.flutter.plugin.common.MethodChannel; 28 | import io.flutter.plugin.common.PluginRegistry; 29 | 30 | /** 31 | * HoverUssdPlugin 32 | */ 33 | 34 | 35 | public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener { 36 | 37 | private Activity activity; 38 | private EventChannel.EventSink transactionEventSink; 39 | private EventChannel.EventSink actionDownloadEventSink; 40 | private BroadcastReceiver smsReceiver; 41 | private BroadcastReceiver transactionStateReceiver; 42 | private HoverUssdApi hoverUssdApi; 43 | private Context context; 44 | private static final String TAG = "HOVER_USSD_PLUGIN"; 45 | 46 | @Override 47 | public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { 48 | MethodChannel channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "HoverUssdChannel"); 49 | EventChannel transactionEventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "TransactionEvent"); 50 | EventChannel actionDownloadEventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "ActionDownloadEvent"); 51 | 52 | context = flutterPluginBinding.getApplicationContext(); 53 | 54 | actionDownloadEventChannel.setStreamHandler(new EventChannel.StreamHandler() { 55 | @Override 56 | public void onListen(Object arguments, EventChannel.EventSink events) { 57 | actionDownloadEventSink = events; 58 | } 59 | 60 | @Override 61 | public void onCancel(Object arguments) { 62 | } 63 | }); 64 | 65 | transactionEventChannel.setStreamHandler(new EventChannel.StreamHandler() { 66 | @Override 67 | public void onListen(Object arguments, EventChannel.EventSink events) { 68 | transactionEventSink = events; 69 | } 70 | 71 | @Override 72 | public void onCancel(Object arguments) { 73 | } 74 | }); 75 | 76 | channel.setMethodCallHandler(this); 77 | } 78 | 79 | @Override 80 | public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { 81 | } 82 | 83 | @Override 84 | public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { 85 | activity = binding.getActivity(); 86 | binding.addActivityResultListener(this); 87 | 88 | smsReceiver = new BroadcastReceiver() { 89 | @Override 90 | public void onReceive(Context context, Intent intent) { 91 | handleSmsReceived(intent); 92 | } 93 | }; 94 | LocalBroadcastManager.getInstance(activity).registerReceiver(smsReceiver, new IntentFilter(activity.getPackageName() + ".SMS_MISS")); 95 | } 96 | 97 | @Override 98 | public void onDetachedFromActivityForConfigChanges() { 99 | activity = null; 100 | } 101 | 102 | @Override 103 | public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { 104 | activity = binding.getActivity(); 105 | binding.addActivityResultListener(this); 106 | } 107 | 108 | @Override 109 | public void onDetachedFromActivity() { 110 | LocalBroadcastManager.getInstance(activity).unregisterReceiver(smsReceiver); 111 | if (transactionStateReceiver != null) { 112 | LocalBroadcastManager.getInstance(activity.getApplication()).unregisterReceiver(transactionStateReceiver); 113 | } 114 | activity = null; 115 | } 116 | 117 | @Override 118 | public boolean onActivityResult(int requestCode, int resultCode, Intent data) { 119 | if (requestCode == 0) { 120 | if (resultCode == Activity.RESULT_OK && data != null) { 121 | handleUssdSuccess(data); 122 | } else if (resultCode == Activity.RESULT_CANCELED && data != null) { 123 | handleUssdFailed(data); 124 | } 125 | return true; 126 | } 127 | return false; 128 | } 129 | 130 | @Override 131 | public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { 132 | hoverUssdApi = new HoverUssdApi(activity, context); 133 | switch (call.method) { 134 | case "Initialize": 135 | initializeHover(call); 136 | break; 137 | case "HasAllPermissions": 138 | result.success(hoverUssdApi.hasAllPerms()); 139 | break; 140 | case "SetPermissionsActivity": 141 | hoverUssdApi.setPermissionsActivity((String) call.argument("activityName")); 142 | result.success(true); 143 | break; 144 | case "HasAccessibilityPermission": 145 | result.success(hoverUssdApi.hasAccessibilityPermission()); 146 | break; 147 | 148 | case "HasSmsPermission": 149 | result.success(hoverUssdApi.hasSmsPermission()); 150 | break; 151 | 152 | case "HasPhonePermission": 153 | result.success(hoverUssdApi.hasPhonePermission()); 154 | break; 155 | case "HasContactsPermission": 156 | result.success(hoverUssdApi.hasContactPermission()); 157 | break; 158 | 159 | case "HasWritePermission": 160 | result.success(hoverUssdApi.hasWritePermission()); 161 | break; 162 | 163 | case "HasOverlayPermission": 164 | result.success(hoverUssdApi.hasOverlayPermission()); 165 | break; 166 | case "getAllActions": 167 | result.success(hoverUssdApi.getAllActions()); 168 | break; 169 | case "getAllTransactions": 170 | result.success(hoverUssdApi.getAllTransaction()); 171 | break; 172 | case "refreshActions": 173 | refreshActions(); 174 | break; 175 | case "HoverStartATransaction": 176 | startHoverTransaction(call, result); 177 | break; 178 | default: 179 | result.notImplemented(); 180 | } 181 | hoverUssdApi = null; 182 | } 183 | 184 | private void initializeHover(MethodCall call) { 185 | hoverUssdApi.initialize(call.argument("apiKey"), call.argument("branding"), call.argument("logo"), call.argument("notificationLogo"), new Hover.DownloadListener() { 186 | @Override 187 | public void onError(String s) { 188 | Map result = new HashMap<>(); 189 | result.put("state", "actionDownloadFailed"); 190 | result.put("error", s); 191 | actionDownloadEventSink.success(result); 192 | } 193 | 194 | @Override 195 | public void onSuccess(ArrayList arrayList) { 196 | Map result = new HashMap<>(); 197 | result.put("state", "actionDownloaded"); 198 | result.put("isDownloaded", true); 199 | actionDownloadEventSink.success(result); 200 | } 201 | }); 202 | } 203 | 204 | private void refreshActions() { 205 | hoverUssdApi.refreshActions(new Hover.DownloadListener() { 206 | @Override 207 | public void onError(String s) { 208 | Map result = new HashMap<>(); 209 | result.put("state", "actionDownloadFailed"); 210 | result.put("error", s); 211 | actionDownloadEventSink.success(result); 212 | } 213 | 214 | @Override 215 | public void onSuccess(ArrayList arrayList) { 216 | Map result = new HashMap<>(); 217 | result.put("state", "actionDownloaded"); 218 | result.put("isDownloaded", true); 219 | actionDownloadEventSink.success(result); 220 | } 221 | }); 222 | } 223 | 224 | private void startHoverTransaction(MethodCall call, MethodChannel.Result result) { 225 | Map resultJson = new HashMap<>(); 226 | resultJson.put("state", "ussdLoading"); 227 | transactionEventSink.success(resultJson); 228 | 229 | transactionStateReceiver = new BroadcastReceiver() { 230 | @Override 231 | public void onReceive(Context context, Intent i) { 232 | transactionEventSink.success(i.getExtras()); 233 | Log.i(TAG, "Received pending transaction created broadcast"); 234 | Log.i(TAG, "uuid: " + i.getStringExtra("uuid")); 235 | } 236 | }; 237 | 238 | try { 239 | hoverUssdApi.sendUssd( 240 | Objects.requireNonNull(call.argument("actionId")), 241 | call.argument("extras") != null ? call.argument("extras") : new HashMap(), 242 | call.argument("theme"), 243 | call.argument("header"), 244 | call.argument("initialProcessingMessage"), 245 | false, 246 | call.argument("finalMsgDisplayTime"), 247 | transactionStateReceiver 248 | ); 249 | result.success(true); 250 | } catch (Exception e) { 251 | Map resultError = new HashMap<>(); 252 | resultError.put("state", "ussdFailed"); 253 | resultError.put("errorMessage", e.getMessage()); 254 | transactionEventSink.success(resultError); 255 | } 256 | } 257 | 258 | private void handleSmsReceived(Intent intent) { 259 | Map result = new HashMap<>(); 260 | result.put("state", "smsParsed"); 261 | result.put("action_id", intentNullAwareString(intent, "action_id")); 262 | result.put("response_message", intentNullAwareString(intent, "response_message")); 263 | result.put("status", intentNullAwareString(intent, "status")); 264 | result.put("status_meaning", intentNullAwareString(intent, "status_meaning")); 265 | result.put("status_description", intentNullAwareString(intent, "status_description")); 266 | result.put("uuid", intentNullAwareString(intent, "uuid")); 267 | result.put("im_hni", intentNullAwareString(intent, "im_hni")); 268 | result.put("environment", intent.getIntExtra("environment", 0)); 269 | result.put("request_timestamp", intent.getIntExtra("request_timestamp", 0)); 270 | result.put("response_timestamp", intent.getIntExtra("response_timestamp", 0)); 271 | result.put("matched_parser_id", intentNullAwareString(intent, "matched_parser_id")); 272 | result.put("messagetype", intentNullAwareString(intent, "messagetype")); 273 | result.put("message_sender", intentNullAwareString(intent, "message_sender")); 274 | result.put("regex", intentNullAwareString(intent, "regex")); 275 | transactionEventSink.success(result); 276 | } 277 | 278 | private void handleUssdSuccess(Intent data) { 279 | String uuid = data.hasExtra("uuid") ? data.getStringExtra("uuid") : ""; 280 | Map result = new HashMap<>(); 281 | result.put("state", "ussdSucceeded"); 282 | result.put("uuid", uuid); 283 | if (data.hasExtra("session_messages")) { 284 | String[] sessionMessages = data.getStringArrayExtra("session_messages"); 285 | result.put("ussdSessionMessages", sessionMessages); 286 | } 287 | transactionEventSink.success(result); 288 | } 289 | 290 | private void handleUssdFailed(Intent data) { 291 | Map result = new HashMap<>(); 292 | result.put("state", "ussdFailed"); 293 | result.put("errorMessage", data.getStringExtra("error")); 294 | transactionEventSink.success(result); 295 | } 296 | 297 | private String intentNullAwareString(Intent intent, String name) { 298 | return intent.hasExtra(name) ? intent.getStringExtra(name) : ""; 299 | } 300 | } -------------------------------------------------------------------------------- /android/src/test/java/com/lucdotdev/hover_ussd/HoverUssdPluginTest.java: -------------------------------------------------------------------------------- 1 | package android.src.test.java.com.lucdotdev.hover_ussd; 2 | 3 | import org.junit.Test; 4 | 5 | /** 6 | * This demonstrates a simple unit test of the Java portion of this plugin's implementation. 7 | * 8 | * Once you have built the plugin's example app, you can run these tests from the command 9 | * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or 10 | * you can run them directly from IDEs that support JUnit such as Android Studio. 11 | */ 12 | 13 | public class HoverUssdPluginTest { 14 | @Test 15 | public void onMethodCall_getPlatformVersion_returnsExpectedValue() { 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/docs/hover.png -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "2f708eb8396e362e280fac22cf171c2cb467343c" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 17 | base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 18 | - platform: android 19 | create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 20 | base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 21 | - platform: ios 22 | create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 23 | base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 24 | - platform: linux 25 | create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 26 | base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 27 | - platform: macos 28 | create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 29 | base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 30 | - platform: web 31 | create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 32 | base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 33 | - platform: windows 34 | create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 35 | base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # hover_ussd_example 2 | 3 | Demonstrates how to use the hover_ussd plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "com.example.example" 27 | compileSdkVersion flutter.compileSdkVersion 28 | ndkVersion flutter.ndkVersion 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.example.example" 46 | // You can update the following values to match your application needs. 47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 48 | minSdkVersion flutter.minSdkVersion 49 | targetSdkVersion flutter.targetSdkVersion 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | 62 | } 63 | 64 | flutter { 65 | source '../..' 66 | } 67 | 68 | dependencies { 69 | 70 | } 71 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java: -------------------------------------------------------------------------------- 1 | // Generated file. 2 | // 3 | // If you wish to remove Flutter's multidex support, delete this entire file. 4 | // 5 | // Modifications to this file should be done in a copy under a different name 6 | // as this file may be regenerated. 7 | 8 | package io.flutter.app; 9 | 10 | import android.app.Application; 11 | import android.content.Context; 12 | import androidx.annotation.CallSuper; 13 | import androidx.multidex.MultiDex; 14 | 15 | /** 16 | * Extension of {@link android.app.Application}, adding multidex support. 17 | */ 18 | public class FlutterMultiDexApplication extends Application { 19 | @Override 20 | @CallSuper 21 | protected void attachBaseContext(Context base) { 22 | super.attachBaseContext(base); 23 | MultiDex.install(this); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | maven { url "https://maven.usehover.com/snapshots" } 7 | } 8 | 9 | 10 | dependencies { 11 | 12 | classpath 'com.android.tools.build:gradle:7.3.0' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | mavenCentral() 22 | maven { url "https://maven.usehover.com/snapshots" } 23 | } 24 | } 25 | 26 | rootProject.buildDir = '../build' 27 | subprojects { 28 | project.buildDir = "${rootProject.buildDir}/${project.name}" 29 | } 30 | subprojects { 31 | project.evaluationDependsOn(':app') 32 | } 33 | 34 | tasks.register("clean", Delete) { 35 | delete rootProject.buildDir 36 | } 37 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | } 9 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | plugins { 14 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false 15 | } 16 | } 17 | 18 | include ":app" 19 | 20 | apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" 21 | -------------------------------------------------------------------------------- /example/integration_test/plugin_integration_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter integration test. 2 | // 3 | // Since integration tests run in a full Flutter application, they can interact 4 | // with the host side of a plugin implementation, unlike Dart unit tests. 5 | // 6 | // For more information about Flutter integration tests, please see 7 | // https://docs.flutter.dev/cookbook/testing/integration/introduction 8 | 9 | 10 | import 'package:flutter_test/flutter_test.dart'; 11 | import 'package:integration_test/integration_test.dart'; 12 | 13 | import 'package:hover_ussd/hover_ussd.dart'; 14 | 15 | void main() { 16 | IntegrationTestWidgetsFlutterBinding.ensureInitialized(); 17 | 18 | testWidgets('getPlatformVersion test', (WidgetTester tester) async { 19 | final HoverUssd plugin = HoverUssd(); 20 | 21 | // The version string depends on the host platform running the test, so 22 | // just assert that some non-empty string is returned. 23 | 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = 97C146E61CF9000F007C117D /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = 97C146ED1CF9000F007C117D; 25 | remoteInfo = Runner; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXCopyFilesBuildPhase section */ 30 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 31 | isa = PBXCopyFilesBuildPhase; 32 | buildActionMask = 2147483647; 33 | dstPath = ""; 34 | dstSubfolderSpec = 10; 35 | files = ( 36 | ); 37 | name = "Embed Frameworks"; 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXCopyFilesBuildPhase section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 44 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 45 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 46 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 47 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 48 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 49 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 50 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 51 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 53 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 54 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 55 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 57 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 9740EEB11CF90186004384FC /* Flutter */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 75 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 76 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 77 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 78 | ); 79 | name = Flutter; 80 | sourceTree = ""; 81 | }; 82 | 331C8082294A63A400263BE5 /* RunnerTests */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 331C807B294A618700263BE5 /* RunnerTests.swift */, 86 | ); 87 | path = RunnerTests; 88 | sourceTree = ""; 89 | }; 90 | 97C146E51CF9000F007C117D = { 91 | isa = PBXGroup; 92 | children = ( 93 | 9740EEB11CF90186004384FC /* Flutter */, 94 | 97C146F01CF9000F007C117D /* Runner */, 95 | 97C146EF1CF9000F007C117D /* Products */, 96 | 331C8082294A63A400263BE5 /* RunnerTests */, 97 | ); 98 | sourceTree = ""; 99 | }; 100 | 97C146EF1CF9000F007C117D /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 97C146EE1CF9000F007C117D /* Runner.app */, 104 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */, 105 | ); 106 | name = Products; 107 | sourceTree = ""; 108 | }; 109 | 97C146F01CF9000F007C117D /* Runner */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 113 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 114 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 115 | 97C147021CF9000F007C117D /* Info.plist */, 116 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 117 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 118 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 119 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 120 | ); 121 | path = Runner; 122 | sourceTree = ""; 123 | }; 124 | /* End PBXGroup section */ 125 | 126 | /* Begin PBXNativeTarget section */ 127 | 331C8080294A63A400263BE5 /* RunnerTests */ = { 128 | isa = PBXNativeTarget; 129 | buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; 130 | buildPhases = ( 131 | 331C807D294A63A400263BE5 /* Sources */, 132 | 331C807E294A63A400263BE5 /* Frameworks */, 133 | 331C807F294A63A400263BE5 /* Resources */, 134 | ); 135 | buildRules = ( 136 | ); 137 | dependencies = ( 138 | 331C8086294A63A400263BE5 /* PBXTargetDependency */, 139 | ); 140 | name = RunnerTests; 141 | productName = RunnerTests; 142 | productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; 143 | productType = "com.apple.product-type.bundle.unit-test"; 144 | }; 145 | 97C146ED1CF9000F007C117D /* Runner */ = { 146 | isa = PBXNativeTarget; 147 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 148 | buildPhases = ( 149 | 9740EEB61CF901F6004384FC /* Run Script */, 150 | 97C146EA1CF9000F007C117D /* Sources */, 151 | 97C146EB1CF9000F007C117D /* Frameworks */, 152 | 97C146EC1CF9000F007C117D /* Resources */, 153 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 154 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 155 | ); 156 | buildRules = ( 157 | ); 158 | dependencies = ( 159 | ); 160 | name = Runner; 161 | productName = Runner; 162 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 163 | productType = "com.apple.product-type.application"; 164 | }; 165 | /* End PBXNativeTarget section */ 166 | 167 | /* Begin PBXProject section */ 168 | 97C146E61CF9000F007C117D /* Project object */ = { 169 | isa = PBXProject; 170 | attributes = { 171 | BuildIndependentTargetsInParallel = YES; 172 | LastUpgradeCheck = 1430; 173 | ORGANIZATIONNAME = ""; 174 | TargetAttributes = { 175 | 331C8080294A63A400263BE5 = { 176 | CreatedOnToolsVersion = 14.0; 177 | TestTargetID = 97C146ED1CF9000F007C117D; 178 | }; 179 | 97C146ED1CF9000F007C117D = { 180 | CreatedOnToolsVersion = 7.3.1; 181 | LastSwiftMigration = 1100; 182 | }; 183 | }; 184 | }; 185 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 186 | compatibilityVersion = "Xcode 9.3"; 187 | developmentRegion = en; 188 | hasScannedForEncodings = 0; 189 | knownRegions = ( 190 | en, 191 | Base, 192 | ); 193 | mainGroup = 97C146E51CF9000F007C117D; 194 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 195 | projectDirPath = ""; 196 | projectRoot = ""; 197 | targets = ( 198 | 97C146ED1CF9000F007C117D /* Runner */, 199 | 331C8080294A63A400263BE5 /* RunnerTests */, 200 | ); 201 | }; 202 | /* End PBXProject section */ 203 | 204 | /* Begin PBXResourcesBuildPhase section */ 205 | 331C807F294A63A400263BE5 /* Resources */ = { 206 | isa = PBXResourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | 97C146EC1CF9000F007C117D /* Resources */ = { 213 | isa = PBXResourcesBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 217 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 218 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 219 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | /* End PBXResourcesBuildPhase section */ 224 | 225 | /* Begin PBXShellScriptBuildPhase section */ 226 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 227 | isa = PBXShellScriptBuildPhase; 228 | alwaysOutOfDate = 1; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", 234 | ); 235 | name = "Thin Binary"; 236 | outputPaths = ( 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | shellPath = /bin/sh; 240 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 241 | }; 242 | 9740EEB61CF901F6004384FC /* Run Script */ = { 243 | isa = PBXShellScriptBuildPhase; 244 | alwaysOutOfDate = 1; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | ); 248 | inputPaths = ( 249 | ); 250 | name = "Run Script"; 251 | outputPaths = ( 252 | ); 253 | runOnlyForDeploymentPostprocessing = 0; 254 | shellPath = /bin/sh; 255 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 256 | }; 257 | /* End PBXShellScriptBuildPhase section */ 258 | 259 | /* Begin PBXSourcesBuildPhase section */ 260 | 331C807D294A63A400263BE5 /* Sources */ = { 261 | isa = PBXSourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, 265 | ); 266 | runOnlyForDeploymentPostprocessing = 0; 267 | }; 268 | 97C146EA1CF9000F007C117D /* Sources */ = { 269 | isa = PBXSourcesBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 273 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | /* End PBXSourcesBuildPhase section */ 278 | 279 | /* Begin PBXTargetDependency section */ 280 | 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { 281 | isa = PBXTargetDependency; 282 | target = 97C146ED1CF9000F007C117D /* Runner */; 283 | targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; 284 | }; 285 | /* End PBXTargetDependency section */ 286 | 287 | /* Begin PBXVariantGroup section */ 288 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 289 | isa = PBXVariantGroup; 290 | children = ( 291 | 97C146FB1CF9000F007C117D /* Base */, 292 | ); 293 | name = Main.storyboard; 294 | sourceTree = ""; 295 | }; 296 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 297 | isa = PBXVariantGroup; 298 | children = ( 299 | 97C147001CF9000F007C117D /* Base */, 300 | ); 301 | name = LaunchScreen.storyboard; 302 | sourceTree = ""; 303 | }; 304 | /* End PBXVariantGroup section */ 305 | 306 | /* Begin XCBuildConfiguration section */ 307 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | ALWAYS_SEARCH_USER_PATHS = NO; 311 | CLANG_ANALYZER_NONNULL = YES; 312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 313 | CLANG_CXX_LIBRARY = "libc++"; 314 | CLANG_ENABLE_MODULES = YES; 315 | CLANG_ENABLE_OBJC_ARC = YES; 316 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 317 | CLANG_WARN_BOOL_CONVERSION = YES; 318 | CLANG_WARN_COMMA = YES; 319 | CLANG_WARN_CONSTANT_CONVERSION = YES; 320 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 321 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 322 | CLANG_WARN_EMPTY_BODY = YES; 323 | CLANG_WARN_ENUM_CONVERSION = YES; 324 | CLANG_WARN_INFINITE_RECURSION = YES; 325 | CLANG_WARN_INT_CONVERSION = YES; 326 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 327 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 328 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 330 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 331 | CLANG_WARN_STRICT_PROTOTYPES = YES; 332 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 333 | CLANG_WARN_UNREACHABLE_CODE = YES; 334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 336 | COPY_PHASE_STRIP = NO; 337 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 338 | ENABLE_NS_ASSERTIONS = NO; 339 | ENABLE_STRICT_OBJC_MSGSEND = YES; 340 | GCC_C_LANGUAGE_STANDARD = gnu99; 341 | GCC_NO_COMMON_BLOCKS = YES; 342 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 343 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 344 | GCC_WARN_UNDECLARED_SELECTOR = YES; 345 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 346 | GCC_WARN_UNUSED_FUNCTION = YES; 347 | GCC_WARN_UNUSED_VARIABLE = YES; 348 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 349 | MTL_ENABLE_DEBUG_INFO = NO; 350 | SDKROOT = iphoneos; 351 | SUPPORTED_PLATFORMS = iphoneos; 352 | TARGETED_DEVICE_FAMILY = "1,2"; 353 | VALIDATE_PRODUCT = YES; 354 | }; 355 | name = Profile; 356 | }; 357 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 358 | isa = XCBuildConfiguration; 359 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 360 | buildSettings = { 361 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 362 | CLANG_ENABLE_MODULES = YES; 363 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 364 | ENABLE_BITCODE = NO; 365 | INFOPLIST_FILE = Runner/Info.plist; 366 | LD_RUNPATH_SEARCH_PATHS = ( 367 | "$(inherited)", 368 | "@executable_path/Frameworks", 369 | ); 370 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 371 | PRODUCT_NAME = "$(TARGET_NAME)"; 372 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 373 | SWIFT_VERSION = 5.0; 374 | VERSIONING_SYSTEM = "apple-generic"; 375 | }; 376 | name = Profile; 377 | }; 378 | 331C8088294A63A400263BE5 /* Debug */ = { 379 | isa = XCBuildConfiguration; 380 | baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */; 381 | buildSettings = { 382 | BUNDLE_LOADER = "$(TEST_HOST)"; 383 | CODE_SIGN_STYLE = Automatic; 384 | CURRENT_PROJECT_VERSION = 1; 385 | GENERATE_INFOPLIST_FILE = YES; 386 | MARKETING_VERSION = 1.0; 387 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 390 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 391 | SWIFT_VERSION = 5.0; 392 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 393 | }; 394 | name = Debug; 395 | }; 396 | 331C8089294A63A400263BE5 /* Release */ = { 397 | isa = XCBuildConfiguration; 398 | baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */; 399 | buildSettings = { 400 | BUNDLE_LOADER = "$(TEST_HOST)"; 401 | CODE_SIGN_STYLE = Automatic; 402 | CURRENT_PROJECT_VERSION = 1; 403 | GENERATE_INFOPLIST_FILE = YES; 404 | MARKETING_VERSION = 1.0; 405 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | SWIFT_VERSION = 5.0; 408 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 409 | }; 410 | name = Release; 411 | }; 412 | 331C808A294A63A400263BE5 /* Profile */ = { 413 | isa = XCBuildConfiguration; 414 | baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */; 415 | buildSettings = { 416 | BUNDLE_LOADER = "$(TEST_HOST)"; 417 | CODE_SIGN_STYLE = Automatic; 418 | CURRENT_PROJECT_VERSION = 1; 419 | GENERATE_INFOPLIST_FILE = YES; 420 | MARKETING_VERSION = 1.0; 421 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; 422 | PRODUCT_NAME = "$(TARGET_NAME)"; 423 | SWIFT_VERSION = 5.0; 424 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 425 | }; 426 | name = Profile; 427 | }; 428 | 97C147031CF9000F007C117D /* Debug */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | ALWAYS_SEARCH_USER_PATHS = NO; 432 | CLANG_ANALYZER_NONNULL = YES; 433 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 434 | CLANG_CXX_LIBRARY = "libc++"; 435 | CLANG_ENABLE_MODULES = YES; 436 | CLANG_ENABLE_OBJC_ARC = YES; 437 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 438 | CLANG_WARN_BOOL_CONVERSION = YES; 439 | CLANG_WARN_COMMA = YES; 440 | CLANG_WARN_CONSTANT_CONVERSION = YES; 441 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 442 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 443 | CLANG_WARN_EMPTY_BODY = YES; 444 | CLANG_WARN_ENUM_CONVERSION = YES; 445 | CLANG_WARN_INFINITE_RECURSION = YES; 446 | CLANG_WARN_INT_CONVERSION = YES; 447 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 448 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 449 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 450 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 451 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 452 | CLANG_WARN_STRICT_PROTOTYPES = YES; 453 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 454 | CLANG_WARN_UNREACHABLE_CODE = YES; 455 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 456 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 457 | COPY_PHASE_STRIP = NO; 458 | DEBUG_INFORMATION_FORMAT = dwarf; 459 | ENABLE_STRICT_OBJC_MSGSEND = YES; 460 | ENABLE_TESTABILITY = YES; 461 | GCC_C_LANGUAGE_STANDARD = gnu99; 462 | GCC_DYNAMIC_NO_PIC = NO; 463 | GCC_NO_COMMON_BLOCKS = YES; 464 | GCC_OPTIMIZATION_LEVEL = 0; 465 | GCC_PREPROCESSOR_DEFINITIONS = ( 466 | "DEBUG=1", 467 | "$(inherited)", 468 | ); 469 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 470 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 471 | GCC_WARN_UNDECLARED_SELECTOR = YES; 472 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 473 | GCC_WARN_UNUSED_FUNCTION = YES; 474 | GCC_WARN_UNUSED_VARIABLE = YES; 475 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 476 | MTL_ENABLE_DEBUG_INFO = YES; 477 | ONLY_ACTIVE_ARCH = YES; 478 | SDKROOT = iphoneos; 479 | TARGETED_DEVICE_FAMILY = "1,2"; 480 | }; 481 | name = Debug; 482 | }; 483 | 97C147041CF9000F007C117D /* Release */ = { 484 | isa = XCBuildConfiguration; 485 | buildSettings = { 486 | ALWAYS_SEARCH_USER_PATHS = NO; 487 | CLANG_ANALYZER_NONNULL = YES; 488 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 489 | CLANG_CXX_LIBRARY = "libc++"; 490 | CLANG_ENABLE_MODULES = YES; 491 | CLANG_ENABLE_OBJC_ARC = YES; 492 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 493 | CLANG_WARN_BOOL_CONVERSION = YES; 494 | CLANG_WARN_COMMA = YES; 495 | CLANG_WARN_CONSTANT_CONVERSION = YES; 496 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 497 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 498 | CLANG_WARN_EMPTY_BODY = YES; 499 | CLANG_WARN_ENUM_CONVERSION = YES; 500 | CLANG_WARN_INFINITE_RECURSION = YES; 501 | CLANG_WARN_INT_CONVERSION = YES; 502 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 503 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 504 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 505 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 506 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 507 | CLANG_WARN_STRICT_PROTOTYPES = YES; 508 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 509 | CLANG_WARN_UNREACHABLE_CODE = YES; 510 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 511 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 512 | COPY_PHASE_STRIP = NO; 513 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 514 | ENABLE_NS_ASSERTIONS = NO; 515 | ENABLE_STRICT_OBJC_MSGSEND = YES; 516 | GCC_C_LANGUAGE_STANDARD = gnu99; 517 | GCC_NO_COMMON_BLOCKS = YES; 518 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 519 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 520 | GCC_WARN_UNDECLARED_SELECTOR = YES; 521 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 522 | GCC_WARN_UNUSED_FUNCTION = YES; 523 | GCC_WARN_UNUSED_VARIABLE = YES; 524 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 525 | MTL_ENABLE_DEBUG_INFO = NO; 526 | SDKROOT = iphoneos; 527 | SUPPORTED_PLATFORMS = iphoneos; 528 | SWIFT_COMPILATION_MODE = wholemodule; 529 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 530 | TARGETED_DEVICE_FAMILY = "1,2"; 531 | VALIDATE_PRODUCT = YES; 532 | }; 533 | name = Release; 534 | }; 535 | 97C147061CF9000F007C117D /* Debug */ = { 536 | isa = XCBuildConfiguration; 537 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 538 | buildSettings = { 539 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 540 | CLANG_ENABLE_MODULES = YES; 541 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 542 | ENABLE_BITCODE = NO; 543 | INFOPLIST_FILE = Runner/Info.plist; 544 | LD_RUNPATH_SEARCH_PATHS = ( 545 | "$(inherited)", 546 | "@executable_path/Frameworks", 547 | ); 548 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 549 | PRODUCT_NAME = "$(TARGET_NAME)"; 550 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 551 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 552 | SWIFT_VERSION = 5.0; 553 | VERSIONING_SYSTEM = "apple-generic"; 554 | }; 555 | name = Debug; 556 | }; 557 | 97C147071CF9000F007C117D /* Release */ = { 558 | isa = XCBuildConfiguration; 559 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 560 | buildSettings = { 561 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 562 | CLANG_ENABLE_MODULES = YES; 563 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 564 | ENABLE_BITCODE = NO; 565 | INFOPLIST_FILE = Runner/Info.plist; 566 | LD_RUNPATH_SEARCH_PATHS = ( 567 | "$(inherited)", 568 | "@executable_path/Frameworks", 569 | ); 570 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 571 | PRODUCT_NAME = "$(TARGET_NAME)"; 572 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 573 | SWIFT_VERSION = 5.0; 574 | VERSIONING_SYSTEM = "apple-generic"; 575 | }; 576 | name = Release; 577 | }; 578 | /* End XCBuildConfiguration section */ 579 | 580 | /* Begin XCConfigurationList section */ 581 | 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { 582 | isa = XCConfigurationList; 583 | buildConfigurations = ( 584 | 331C8088294A63A400263BE5 /* Debug */, 585 | 331C8089294A63A400263BE5 /* Release */, 586 | 331C808A294A63A400263BE5 /* Profile */, 587 | ); 588 | defaultConfigurationIsVisible = 0; 589 | defaultConfigurationName = Release; 590 | }; 591 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 592 | isa = XCConfigurationList; 593 | buildConfigurations = ( 594 | 97C147031CF9000F007C117D /* Debug */, 595 | 97C147041CF9000F007C117D /* Release */, 596 | 249021D3217E4FDB00AE95B9 /* Profile */, 597 | ); 598 | defaultConfigurationIsVisible = 0; 599 | defaultConfigurationName = Release; 600 | }; 601 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 602 | isa = XCConfigurationList; 603 | buildConfigurations = ( 604 | 97C147061CF9000F007C117D /* Debug */, 605 | 97C147071CF9000F007C117D /* Release */, 606 | 249021D4217E4FDB00AE95B9 /* Profile */, 607 | ); 608 | defaultConfigurationIsVisible = 0; 609 | defaultConfigurationName = Release; 610 | }; 611 | /* End XCConfigurationList section */ 612 | }; 613 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 614 | } 615 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucdotdev/hover_ussd/b13311910616d8ee37f250a24f3cce0f6c99ff91/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:hover_ussd/hover_ussd.dart'; 5 | 6 | void main() { 7 | runApp(const MyApp()); 8 | } 9 | 10 | class MyApp extends StatelessWidget { 11 | const MyApp({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return MaterialApp( 16 | title: 'Hover USSD Example', 17 | theme: ThemeData( 18 | primarySwatch: Colors.blue, 19 | visualDensity: VisualDensity.adaptivePlatformDensity, 20 | ), 21 | home: const MyHomePage(), 22 | ); 23 | } 24 | } 25 | 26 | class MyHomePage extends StatefulWidget { 27 | const MyHomePage({super.key}); 28 | 29 | @override 30 | State createState() => _MyHomePageState(); 31 | } 32 | 33 | class _MyHomePageState extends State { 34 | late final HoverUssd _hoverUssd = HoverUssd(); 35 | late StreamSubscription _transactionListening; 36 | late StreamSubscription _actionDownloadListening; 37 | bool _hasPermissions = false; 38 | bool _isOverlayEnabled = false; 39 | bool _isAccessibilityEnabled = false; 40 | 41 | final String activityName = "com.hover.sdk.permissions.PermissionActivity"; 42 | 43 | @override 44 | void initState() { 45 | _transactionListening = 46 | _hoverUssd.getUssdTransactionState().listen((event) { 47 | ScaffoldMessenger.of(context) 48 | .showSnackBar(SnackBar(content: Text(event.toMap().toString()))); 49 | }); 50 | 51 | _actionDownloadListening = 52 | _hoverUssd.getDownloadActionState().listen((event) { 53 | ScaffoldMessenger.of(context) 54 | .showSnackBar(SnackBar(content: Text(event.toString()))); 55 | }); 56 | _checkAccessibility(); 57 | _checkPermissions(); 58 | _checkOverlay(); 59 | _actionDownloadListening.onData((event) { 60 | _initHover(); 61 | }); 62 | 63 | super.initState(); 64 | } 65 | 66 | @override 67 | void dispose() { 68 | _transactionListening.cancel(); 69 | _actionDownloadListening.cancel(); 70 | super.dispose(); 71 | } 72 | //check for permissions 73 | 74 | Future _checkPermissions() async { 75 | _hasPermissions = await _hoverUssd.hasAllPermissions(); 76 | setState(() {}); 77 | } 78 | 79 | Future _checkAccessibility() async { 80 | _isAccessibilityEnabled = await _hoverUssd.hasSmsPermission(); 81 | setState(() {}); 82 | } 83 | 84 | Future _checkOverlay() async { 85 | _isOverlayEnabled = await _hoverUssd.hasOverlayPermission(); 86 | setState(() {}); 87 | } 88 | 89 | Future _initHover() async { 90 | try { 91 | await _hoverUssd.initialize( 92 | apiKey: "15ccc2bd81801d8c5fbfd5847d3b4e77", 93 | branding: "My Hover App", 94 | logo: "ic_launcher", 95 | notificationLogo: "ic_launcher", 96 | ); 97 | 98 | _hoverUssd.setPermissionsActivity(activityName: activityName); 99 | } catch (e) { 100 | print(e); 101 | } 102 | } 103 | 104 | Future _getAndGoToActions() async { 105 | final actions = await _hoverUssd.getAllActions(); 106 | Navigator.push( 107 | context, 108 | MaterialPageRoute( 109 | builder: (context) => HoverActionListPage( 110 | hoverActions: actions, 111 | ), 112 | ), 113 | ); 114 | } 115 | 116 | @override 117 | Widget build(BuildContext context) { 118 | return Scaffold( 119 | appBar: AppBar( 120 | title: const Text('Hover Ussd Example'), 121 | ), 122 | body: Center( 123 | child: Column( 124 | mainAxisAlignment: MainAxisAlignment.center, 125 | crossAxisAlignment: CrossAxisAlignment.center, 126 | children: [ 127 | ElevatedButton( 128 | onPressed: () { 129 | _hoverUssd.startTransaction( 130 | actionId: "c6e45e62", 131 | extras: {"price": "4000"}, 132 | theme: "HoverTheme", 133 | header: "Hover Ussd Example", 134 | showUserStepDescriptions: true, 135 | ); 136 | }, 137 | child: const Text("Start Transaction"), 138 | ), 139 | ElevatedButton( 140 | onPressed: _getAndGoToActions, 141 | child: const Text("Get Actions"), 142 | ), 143 | 144 | //refresh actions 145 | ElevatedButton( 146 | onPressed: () { 147 | _hoverUssd.refreshActions(); 148 | }, 149 | child: const Text("Refresh Actions"), 150 | ), 151 | Text( 152 | _hasPermissions 153 | ? "Permissions Granted" 154 | : "Permissions Not Granted", 155 | style: TextStyle( 156 | color: _hasPermissions ? Colors.green : Colors.red, 157 | fontWeight: FontWeight.bold, 158 | ), 159 | ), 160 | Text( 161 | _isAccessibilityEnabled 162 | ? "Accessibility Granted" 163 | : "Accessibility Not Granted", 164 | style: TextStyle( 165 | color: _isAccessibilityEnabled ? Colors.green : Colors.red, 166 | fontWeight: FontWeight.bold, 167 | ), 168 | ), 169 | Text( 170 | _isOverlayEnabled ? "Overlay Granted" : "Overlay Not Granted", 171 | style: TextStyle( 172 | color: _isOverlayEnabled ? Colors.green : Colors.red, 173 | fontWeight: FontWeight.bold, 174 | ), 175 | ), 176 | 177 | StreamBuilder( 178 | stream: _hoverUssd.getDownloadActionState(), 179 | builder: (context, snapshot) { 180 | final state = snapshot.data; 181 | 182 | String statusText; 183 | Color textColor; 184 | 185 | if (state is ActionDownloaded) { 186 | statusText = "Actions Downloaded"; 187 | textColor = Colors.green; 188 | } else if (state is ActionDownloadFailed) { 189 | statusText = "Actions Not Downloaded"; 190 | textColor = Colors.red; 191 | } else if (state is ActionDownloading) { 192 | statusText = "Actions Downloading"; 193 | textColor = 194 | Colors.blue; // Adjust color as per your preference 195 | } else { 196 | statusText = "Action Download Status: Unknown"; 197 | textColor = Colors.grey; 198 | } 199 | 200 | return Text( 201 | statusText, 202 | style: 203 | TextStyle(color: textColor, fontWeight: FontWeight.bold), 204 | ); 205 | }, 206 | ), 207 | StreamBuilder( 208 | stream: _hoverUssd.getUssdTransactionState(), 209 | builder: (BuildContext context, snapshot) { 210 | if (snapshot.data is SmsParsed) { 211 | return Text("Sms parsed : \n${snapshot.data!.toMap()}"); 212 | } 213 | if (snapshot.data is UssdSucceeded) { 214 | return Text("Ussd Succeded : \n${snapshot.data!.toMap()}"); 215 | } 216 | if (snapshot.data is UssdLoading) { 217 | return const Text("loading..."); 218 | } 219 | if (snapshot.data is UssdFailed) { 220 | return Text("Ussd Failed : \n${snapshot.data!.toMap()}"); 221 | } 222 | if (snapshot.data is EmptyState) { 223 | return const Text("Empty State"); 224 | } 225 | return const Text("No state"); 226 | }, 227 | ), 228 | ], 229 | ), 230 | ), 231 | ); 232 | } 233 | } 234 | 235 | class HoverActionListPage extends StatefulWidget { 236 | final List hoverActions; 237 | 238 | const HoverActionListPage({Key? key, required this.hoverActions}) 239 | : super(key: key); 240 | 241 | @override 242 | State createState() => _HoverActionListPageState(); 243 | } 244 | 245 | class _HoverActionListPageState extends State { 246 | @override 247 | Widget build(BuildContext context) { 248 | return Scaffold( 249 | appBar: AppBar( 250 | title: const Text('Hover Action List'), 251 | ), 252 | body: ListView.builder( 253 | itemCount: widget.hoverActions.length, 254 | itemBuilder: (BuildContext context, int index) { 255 | final hoverAction = widget.hoverActions[index]; 256 | return ListTile( 257 | title: Text(hoverAction.name ?? ''), 258 | subtitle: Column( 259 | crossAxisAlignment: CrossAxisAlignment.start, 260 | children: [ 261 | Text('Network: ${hoverAction.networkName ?? ''}'), 262 | Text('Country: ${hoverAction.countryAlpha2 ?? ''}'), 263 | Text('Transaction Type: ${hoverAction.transactionType ?? ''}'), 264 | // Add more details as needed 265 | ], 266 | ), 267 | onTap: () { 268 | // Handle onTap if needed 269 | }, 270 | ); 271 | }, 272 | ), 273 | ); 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.3.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.18.0" 44 | cupertino_icons: 45 | dependency: "direct main" 46 | description: 47 | name: cupertino_icons 48 | sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.0.6" 52 | fake_async: 53 | dependency: transitive 54 | description: 55 | name: fake_async 56 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.3.1" 60 | file: 61 | dependency: transitive 62 | description: 63 | name: file 64 | sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "7.0.0" 68 | flutter: 69 | dependency: "direct main" 70 | description: flutter 71 | source: sdk 72 | version: "0.0.0" 73 | flutter_driver: 74 | dependency: transitive 75 | description: flutter 76 | source: sdk 77 | version: "0.0.0" 78 | flutter_lints: 79 | dependency: "direct dev" 80 | description: 81 | name: flutter_lints 82 | sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 83 | url: "https://pub.dev" 84 | source: hosted 85 | version: "2.0.3" 86 | flutter_test: 87 | dependency: "direct dev" 88 | description: flutter 89 | source: sdk 90 | version: "0.0.0" 91 | fuchsia_remote_debug_protocol: 92 | dependency: transitive 93 | description: flutter 94 | source: sdk 95 | version: "0.0.0" 96 | hover_ussd: 97 | dependency: "direct main" 98 | description: 99 | path: ".." 100 | relative: true 101 | source: path 102 | version: "2.0.0" 103 | integration_test: 104 | dependency: "direct dev" 105 | description: flutter 106 | source: sdk 107 | version: "0.0.0" 108 | leak_tracker: 109 | dependency: transitive 110 | description: 111 | name: leak_tracker 112 | sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "10.0.0" 116 | leak_tracker_flutter_testing: 117 | dependency: transitive 118 | description: 119 | name: leak_tracker_flutter_testing 120 | sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "2.0.1" 124 | leak_tracker_testing: 125 | dependency: transitive 126 | description: 127 | name: leak_tracker_testing 128 | sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 129 | url: "https://pub.dev" 130 | source: hosted 131 | version: "2.0.1" 132 | lints: 133 | dependency: transitive 134 | description: 135 | name: lints 136 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 137 | url: "https://pub.dev" 138 | source: hosted 139 | version: "2.1.1" 140 | matcher: 141 | dependency: transitive 142 | description: 143 | name: matcher 144 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 145 | url: "https://pub.dev" 146 | source: hosted 147 | version: "0.12.16+1" 148 | material_color_utilities: 149 | dependency: transitive 150 | description: 151 | name: material_color_utilities 152 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" 153 | url: "https://pub.dev" 154 | source: hosted 155 | version: "0.8.0" 156 | meta: 157 | dependency: transitive 158 | description: 159 | name: meta 160 | sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 161 | url: "https://pub.dev" 162 | source: hosted 163 | version: "1.11.0" 164 | path: 165 | dependency: transitive 166 | description: 167 | name: path 168 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 169 | url: "https://pub.dev" 170 | source: hosted 171 | version: "1.9.0" 172 | platform: 173 | dependency: transitive 174 | description: 175 | name: platform 176 | sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" 177 | url: "https://pub.dev" 178 | source: hosted 179 | version: "3.1.4" 180 | plugin_platform_interface: 181 | dependency: transitive 182 | description: 183 | name: plugin_platform_interface 184 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 185 | url: "https://pub.dev" 186 | source: hosted 187 | version: "2.1.8" 188 | process: 189 | dependency: transitive 190 | description: 191 | name: process 192 | sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" 193 | url: "https://pub.dev" 194 | source: hosted 195 | version: "5.0.2" 196 | sky_engine: 197 | dependency: transitive 198 | description: flutter 199 | source: sdk 200 | version: "0.0.99" 201 | source_span: 202 | dependency: transitive 203 | description: 204 | name: source_span 205 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 206 | url: "https://pub.dev" 207 | source: hosted 208 | version: "1.10.0" 209 | stack_trace: 210 | dependency: transitive 211 | description: 212 | name: stack_trace 213 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 214 | url: "https://pub.dev" 215 | source: hosted 216 | version: "1.11.1" 217 | stream_channel: 218 | dependency: transitive 219 | description: 220 | name: stream_channel 221 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 222 | url: "https://pub.dev" 223 | source: hosted 224 | version: "2.1.2" 225 | string_scanner: 226 | dependency: transitive 227 | description: 228 | name: string_scanner 229 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 230 | url: "https://pub.dev" 231 | source: hosted 232 | version: "1.2.0" 233 | sync_http: 234 | dependency: transitive 235 | description: 236 | name: sync_http 237 | sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" 238 | url: "https://pub.dev" 239 | source: hosted 240 | version: "0.3.1" 241 | term_glyph: 242 | dependency: transitive 243 | description: 244 | name: term_glyph 245 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 246 | url: "https://pub.dev" 247 | source: hosted 248 | version: "1.2.1" 249 | test_api: 250 | dependency: transitive 251 | description: 252 | name: test_api 253 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" 254 | url: "https://pub.dev" 255 | source: hosted 256 | version: "0.6.1" 257 | vector_math: 258 | dependency: transitive 259 | description: 260 | name: vector_math 261 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 262 | url: "https://pub.dev" 263 | source: hosted 264 | version: "2.1.4" 265 | vm_service: 266 | dependency: transitive 267 | description: 268 | name: vm_service 269 | sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 270 | url: "https://pub.dev" 271 | source: hosted 272 | version: "13.0.0" 273 | webdriver: 274 | dependency: transitive 275 | description: 276 | name: webdriver 277 | sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" 278 | url: "https://pub.dev" 279 | source: hosted 280 | version: "3.0.3" 281 | sdks: 282 | dart: ">=3.2.0-0 <4.0.0" 283 | flutter: ">=3.3.0" 284 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: hover_ussd_example 2 | description: Demonstrates how to use the hover_ussd plugin. 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | environment: 8 | sdk: '>=3.1.3 <4.0.0' 9 | 10 | # Dependencies specify other packages that your package needs in order to work. 11 | # To automatically upgrade your package dependencies to the latest versions 12 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 13 | # dependencies can be manually updated by changing the version numbers below to 14 | # the latest version available on pub.dev. To see which dependencies have newer 15 | # versions available, run `flutter pub outdated`. 16 | dependencies: 17 | flutter: 18 | sdk: flutter 19 | 20 | hover_ussd: 21 | # When depending on this package from a real application you should use: 22 | # hover_ussd: ^x.y.z 23 | # See https://dart.dev/tools/pub/dependencies#version-constraints 24 | # The example app is bundled with the plugin so we use a path dependency on 25 | # the parent directory to use the current plugin's version. 26 | path: ../ 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^1.0.2 31 | 32 | dev_dependencies: 33 | integration_test: 34 | sdk: flutter 35 | flutter_test: 36 | sdk: flutter 37 | 38 | # The "flutter_lints" package below contains a set of recommended lints to 39 | # encourage good coding practices. The lint set provided by the package is 40 | # activated in the `analysis_options.yaml` file located at the root of your 41 | # package. See that file for information about deactivating specific lint 42 | # rules and activating additional ones. 43 | flutter_lints: ^2.0.0 44 | 45 | # For information on the generic Dart part of this file, see the 46 | # following page: https://dart.dev/tools/pub/pubspec 47 | 48 | # The following section is specific to Flutter packages. 49 | flutter: 50 | 51 | # The following line ensures that the Material Icons font is 52 | # included with your application, so that you can use the icons in 53 | # the material Icons class. 54 | uses-material-design: true 55 | 56 | # To add assets to your application, add an assets section, like this: 57 | # assets: 58 | # - images/a_dot_burr.jpeg 59 | # - images/a_dot_ham.jpeg 60 | 61 | # An image asset can refer to one or more resolution-specific "variants", see 62 | # https://flutter.dev/assets-and-images/#resolution-aware 63 | 64 | # For details regarding adding assets from package dependencies, see 65 | # https://flutter.dev/assets-and-images/#from-packages 66 | 67 | # To add custom fonts to your application, add a fonts section here, 68 | # in this "flutter" section. Each entry in this list should have a 69 | # "family" key with the font family name, and a "fonts" key with a 70 | # list giving the asset and other descriptors for the font. For 71 | # example: 72 | # fonts: 73 | # - family: Schyler 74 | # fonts: 75 | # - asset: fonts/Schyler-Regular.ttf 76 | # - asset: fonts/Schyler-Italic.ttf 77 | # style: italic 78 | # - family: Trajan Pro 79 | # fonts: 80 | # - asset: fonts/TrajanPro.ttf 81 | # - asset: fonts/TrajanPro_Bold.ttf 82 | # weight: 700 83 | # 84 | # For details regarding fonts from package dependencies, 85 | # see https://flutter.dev/custom-fonts/#from-packages 86 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:hover_ussd_example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Verify Platform version', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | 17 | // Verify that platform version is retrieved. 18 | expect( 19 | find.byWidgetPredicate( 20 | (Widget widget) => widget is Text && 21 | widget.data!.startsWith('Running on:'), 22 | ), 23 | findsOneWidget, 24 | ); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /hover_ussd.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /lib/hover_ussd.dart: -------------------------------------------------------------------------------- 1 | export 'hover_ussd_plugin.dart'; 2 | export 'models/transaction.dart'; 3 | export 'models/transaction_state.dart'; 4 | export 'models/hover_action.dart'; 5 | export 'models/download_action_state.dart'; -------------------------------------------------------------------------------- /lib/hover_ussd_plugin.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/services.dart'; 4 | import 'package:hover_ussd/hover_ussd.dart'; 5 | 6 | class HoverUssd { 7 | static const MethodChannel _methodChannel = MethodChannel('HoverUssdChannel'); 8 | static const EventChannel _transactionEventChannel = 9 | EventChannel('TransactionEvent'); 10 | static const EventChannel _downloadEventChannel = 11 | EventChannel('ActionDownloadEvent'); 12 | 13 | Stream? _onTransactionStateChanged; 14 | 15 | /// Initialize HoverUssd with branding and logo information. 16 | /// 17 | /// [apiKey] is the Hover API key. 18 | /// [branding] is the name of the app. 19 | /// [logo] is the id of the app's logo in ressource. 20 | /// [notificationLogo] is the id of the app's logo in ressource. 21 | /// 22 | Future initialize({ 23 | required String apiKey, 24 | String? branding, 25 | String? logo, 26 | String? notificationLogo, 27 | }) async { 28 | await _methodChannel.invokeMethod("Initialize", { 29 | "branding": branding ?? "Flutter App", 30 | "logo": logo ?? "ic_launcher", 31 | "notificationLogo": notificationLogo ?? "ic_launcher", 32 | "apiKey": apiKey, 33 | }); 34 | } 35 | 36 | /// Start a transaction with the provided parameters. 37 | /// [actionId] is the id of the action to be performed. 38 | /// [extras] is a map of extra parameters to be sent to the action. 39 | /// [theme] is the theme of the transaction. 40 | /// [header] is the header of the transaction. 41 | /// [initialProcessingMessage] is the message to be displayed while the transaction is being processed. 42 | /// [finalMsgDisplayTime] is the time to display the final message. 43 | /// [showUserStepDescriptions] is a flag to show the user step descriptions. 44 | /// 45 | Future startTransaction({ 46 | required String actionId, 47 | Map? extras, 48 | String? theme, 49 | String? header, 50 | String? initialProcessingMessage, 51 | int finalMsgDisplayTime = 5000, 52 | bool? showUserStepDescriptions, 53 | }) async { 54 | await _methodChannel.invokeMethod("HoverStartATransaction", { 55 | "actionId": actionId, 56 | "extras": extras ?? {}, 57 | "theme": theme, 58 | "header": header, 59 | "initialProcessingMessage": initialProcessingMessage, 60 | "finalMsgDisplayTime": finalMsgDisplayTime, 61 | }); 62 | } 63 | 64 | /// Check if the app has all the necessary permissions. 65 | Future hasAllPermissions() async { 66 | return await _methodChannel.invokeMethod("HasAllPermissions"); 67 | } 68 | 69 | // Check accessibility permission 70 | Future hasAccessibilityPermission() async { 71 | return await _methodChannel.invokeMethod('HasAccessibilityPermission'); 72 | } 73 | 74 | // Check SMS permission 75 | Future hasSmsPermission() async { 76 | return await _methodChannel.invokeMethod('HasSmsPermission'); 77 | } 78 | 79 | // Check phone permission 80 | Future hasPhonePermission() async { 81 | return await _methodChannel.invokeMethod('HasPhonePermission'); 82 | } 83 | 84 | // Check contacts permission 85 | Future hasContactsPermission() async { 86 | return await _methodChannel.invokeMethod('HasContactsPermission'); 87 | } 88 | 89 | // Check write permission 90 | Future hasWritePermission() async { 91 | return await _methodChannel.invokeMethod('HasWritePermission'); 92 | } 93 | 94 | // Check overlay permission 95 | Future hasOverlayPermission() async { 96 | return await _methodChannel.invokeMethod('HasOverlayPermission'); 97 | } 98 | 99 | /// this set custom permissions activity for the app 100 | /// [activityName] is the name of the custom activity to be set ex: 'com.example.example.CustomPermissionsActivity' 101 | Future setPermissionsActivity({required String activityName}) async { 102 | _methodChannel.invokeMethod("SetPermissionsActivity"); 103 | } 104 | 105 | /// Check if the app has downloaded actions. 106 | Future hasActionsDownloaded() async { 107 | return await _methodChannel.invokeMethod("HasActionsDownloaded"); 108 | } 109 | 110 | /// Retrieve a list of available actions. 111 | Future> getAllActions() async { 112 | try { 113 | final result = await _methodChannel.invokeListMethod("getAllActions"); 114 | if (result != null) { 115 | List actions = result 116 | .map((item) => HoverAction.fromMap(item.cast())) 117 | .toList(); 118 | return actions; 119 | } else { 120 | return []; 121 | } 122 | } catch (e) { 123 | print('Error retrieving actions: $e'); 124 | return []; 125 | } 126 | } 127 | 128 | /// refresh actions 129 | Future refreshActions() async { 130 | await _methodChannel.invokeMethod("refreshActions"); 131 | } 132 | 133 | Future> getAllTransactions() async { 134 | try { 135 | final result = 136 | await _methodChannel.invokeListMethod("getAllTransactions"); 137 | if (result != null) { 138 | final List> resultList = 139 | List>.from(result); 140 | List transactions = 141 | resultList.map((item) => Transaction.fromMap(item)).toList(); 142 | return transactions; 143 | } else { 144 | return []; 145 | } 146 | } catch (e) { 147 | print('Error retrieving transactions: $e'); 148 | return []; 149 | } 150 | } 151 | 152 | /// Get the stream of transaction states. 153 | Stream getUssdTransactionState() { 154 | if (_onTransactionStateChanged == null) { 155 | _onTransactionStateChanged ??= _transactionEventChannel 156 | .receiveBroadcastStream() 157 | .map((dynamic event) => _parseTransactionState( 158 | event['state'], 159 | Map.from(event), 160 | )) 161 | .asBroadcastStream(); 162 | } 163 | 164 | return _onTransactionStateChanged!; 165 | } 166 | 167 | Stream getDownloadActionState() { 168 | return _downloadEventChannel 169 | .receiveBroadcastStream() 170 | .map((dynamic event) => _parseDownloadActionState( 171 | event['state'], 172 | Map.from(event), 173 | )) 174 | .asBroadcastStream(); 175 | } 176 | 177 | DonwloadActionState _parseDownloadActionState( 178 | String state, 179 | Map result, 180 | ) { 181 | switch (state) { 182 | case "actionDownloaded": 183 | return ActionDownloaded.fromMap(result); 184 | case "actionDownloadFailed": 185 | return ActionDownloadFailed.fromMap(result); 186 | case "actionDownloading": 187 | return ActionDownloading(); 188 | default: 189 | return EmptyDownloadState(); 190 | } 191 | } 192 | 193 | /// Parse transaction state from received event. 194 | TransactionState _parseTransactionState( 195 | String state, 196 | Map result, 197 | ) { 198 | print("State: $state"); 199 | switch (state) { 200 | case "smsParsed": 201 | return SmsParsed.fromMap(result); 202 | case "ussdSucceeded": 203 | return UssdSucceeded.fromMap(result); 204 | case "ussdFailed": 205 | return UssdFailed.fromMap(result); 206 | case "ussdLoading": 207 | return UssdLoading(); 208 | default: 209 | return EmptyState(); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /lib/models/download_action_state.dart: -------------------------------------------------------------------------------- 1 | abstract class DonwloadActionState { 2 | const DonwloadActionState(); 3 | Map toMap(); 4 | } 5 | 6 | class ActionDownloadFailed extends DonwloadActionState { 7 | final String error; 8 | 9 | ActionDownloadFailed(this.error); 10 | 11 | @override 12 | Map toMap() { 13 | return { 14 | "state": "actionDownloadFailed", 15 | "error": error, 16 | }; 17 | } 18 | 19 | factory ActionDownloadFailed.fromMap(Map map) { 20 | return ActionDownloadFailed(map['error']); 21 | } 22 | } 23 | 24 | class ActionDownloading extends DonwloadActionState { 25 | @override 26 | Map toMap() { 27 | return { 28 | "state": "actionDownloading", 29 | }; 30 | } 31 | } 32 | 33 | class ActionDownloaded extends DonwloadActionState { 34 | final bool isDownloaded; 35 | 36 | ActionDownloaded(this.isDownloaded); 37 | 38 | @override 39 | Map toMap() { 40 | return { 41 | "state": "actionDownloaded", 42 | "isDownloaded": isDownloaded, 43 | }; 44 | } 45 | 46 | factory ActionDownloaded.fromMap(Map map) { 47 | return ActionDownloaded(map['isDownloaded']); 48 | } 49 | 50 | } 51 | 52 | class EmptyDownloadState extends DonwloadActionState { 53 | @override 54 | Map toMap() { 55 | return { 56 | "state": "empty", 57 | }; 58 | } 59 | } -------------------------------------------------------------------------------- /lib/models/hover_action.dart: -------------------------------------------------------------------------------- 1 | class HoverAction { 2 | int? id; 3 | String? publicId; 4 | String? name; 5 | int? channelId; 6 | String? networkName; 7 | String? countryAlpha2; 8 | String? rootCode; 9 | String? transportType; 10 | String? transactionType; 11 | int? fromInstitutionId; 12 | String? fromInstitutionName; 13 | String? fromInstitutionLogo; 14 | int? toInstitutionId; 15 | String? toInstitutionName; 16 | String? toInstitutionLogo; 17 | String? toCountryAlpha2; 18 | int? createdTimestamp; 19 | int? updatedTimestamp; 20 | int? bountyAmount; 21 | bool? bountyIsOpen; 22 | bool? isReady; 23 | int? bonusPercent; 24 | String? bonusMessage; 25 | List? hniList; 26 | List? customSteps; 27 | List? tagsList; 28 | Map? requiredParams; 29 | Map? outputParams; 30 | 31 | HoverAction({ 32 | this.id, 33 | this.publicId, 34 | this.name, 35 | this.channelId, 36 | this.networkName, 37 | this.countryAlpha2, 38 | this.rootCode, 39 | this.transportType, 40 | this.transactionType, 41 | this.fromInstitutionId, 42 | this.fromInstitutionName, 43 | this.fromInstitutionLogo, 44 | this.toInstitutionId, 45 | this.toInstitutionName, 46 | this.toInstitutionLogo, 47 | this.toCountryAlpha2, 48 | this.createdTimestamp, 49 | this.updatedTimestamp, 50 | this.bountyAmount, 51 | this.bountyIsOpen, 52 | this.isReady, 53 | this.bonusPercent, 54 | this.bonusMessage, 55 | this.hniList, 56 | this.customSteps, 57 | this.tagsList, 58 | this.requiredParams, 59 | this.outputParams, 60 | }); 61 | 62 | factory HoverAction.fromMap(Map json) { 63 | return HoverAction( 64 | id: json['id'], 65 | publicId: json['public_id'], 66 | name: json['name'], 67 | channelId: json['channel_id'], 68 | networkName: json['network_name'], 69 | countryAlpha2: json['country_alpha2'], 70 | rootCode: json['root_code'], 71 | transportType: json['transport_type'], 72 | transactionType: json['transaction_type'], 73 | fromInstitutionId: json['from_institution_id'], 74 | fromInstitutionName: json['from_institution_name'], 75 | fromInstitutionLogo: json['from_institution_logo'], 76 | toInstitutionId: json['to_institution_id'], 77 | toInstitutionName: json['to_institution_name'], 78 | toInstitutionLogo: json['to_institution_logo'], 79 | toCountryAlpha2: json['to_country_alpha2'], 80 | createdTimestamp: json['created_timestamp'], 81 | updatedTimestamp: json['updated_timestamp'], 82 | bountyAmount: json['bounty_amount'], 83 | bountyIsOpen: json['bounty_is_open'], 84 | isReady: json['is_ready'], 85 | bonusPercent: json['bonus_percent'], 86 | bonusMessage: json['bonus_message'], 87 | requiredParams: json['required_params'], 88 | outputParams: json['output_params'], 89 | ); 90 | } 91 | 92 | Map toMap() { 93 | return { 94 | 'id': id, 95 | 'public_id': publicId, 96 | 'name': name, 97 | 'channel_id': channelId, 98 | 'network_name': networkName, 99 | 'country_alpha2': countryAlpha2, 100 | 'root_code': rootCode, 101 | 'transport_type': transportType, 102 | 'transaction_type': transactionType, 103 | 'from_institution_id': fromInstitutionId, 104 | 'from_institution_name': fromInstitutionName, 105 | 'from_institution_logo': fromInstitutionLogo, 106 | 'to_institution_id': toInstitutionId, 107 | 'to_institution_name': toInstitutionName, 108 | 'to_institution_logo': toInstitutionLogo, 109 | 'to_country_alpha2': toCountryAlpha2, 110 | 'created_timestamp': createdTimestamp, 111 | 'updated_timestamp': updatedTimestamp, 112 | 'bounty_amount': bountyAmount, 113 | 'bounty_is_open': bountyIsOpen, 114 | 'is_ready': isReady, 115 | 'bonus_percent': bonusPercent, 116 | 'bonus_message': bonusMessage, 117 | }; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /lib/models/transaction.dart: -------------------------------------------------------------------------------- 1 | class Transaction { 2 | final String? id; 3 | final String? actionId; 4 | final String? uuid; 5 | final String? status; 6 | final String? category; 7 | final String? userMessage; 8 | final String? networkHni; 9 | final String? inputExtras; 10 | final String? parsedVariables; 11 | final List? ussdMessages; 12 | final List? enteredValues; 13 | final List? smsHits; 14 | final List? smsMisses; 15 | final List? logMessages; 16 | final List? matchedParsers; 17 | final int? reqTimestamp; 18 | final int? updatedTimestamp; 19 | 20 | Transaction( 21 | {this.uuid, 22 | this.id, 23 | this.actionId, 24 | this.status, 25 | this.category, 26 | this.userMessage, 27 | this.networkHni, 28 | this.inputExtras, 29 | this.parsedVariables, 30 | this.ussdMessages, 31 | this.enteredValues, 32 | this.smsHits, 33 | this.smsMisses, 34 | this.logMessages, 35 | this.matchedParsers, 36 | this.reqTimestamp, 37 | this.updatedTimestamp}); 38 | 39 | factory Transaction.fromMap(Map map) { 40 | return Transaction( 41 | uuid: map['uuid'], 42 | id: map['id'], 43 | actionId: map['actionId'], 44 | status: map['status'], 45 | category: map['category'], 46 | userMessage: map['userMessage'], 47 | networkHni: map['networkHni'], 48 | inputExtras: map['inputExtras'], 49 | parsedVariables: map['parsedVariables'], 50 | ussdMessages: map['ussdMessages'], 51 | enteredValues: map['enteredValues'], 52 | smsHits: map['smsHits'], 53 | smsMisses: map['smsMisses'], 54 | logMessages: map['logMessages'], 55 | matchedParsers: map['matchedParsers'], 56 | reqTimestamp: map['reqTimestamp'], 57 | updatedTimestamp: map['updatedTimestamp'], 58 | ); 59 | } 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /lib/models/transaction_state.dart: -------------------------------------------------------------------------------- 1 | abstract class TransactionState { 2 | TransactionState(); 3 | 4 | Map toMap(); 5 | } 6 | 7 | /// when the message(sms) is successfully parsed 8 | class SmsParsed extends TransactionState { 9 | /// Unique Identifier for the transaction 10 | final String? uuid; 11 | 12 | /// The action id from out supported operators page 13 | final String? actionId; 14 | 15 | /// Full message used for parsing 16 | final String? responseMessage; 17 | 18 | /// “pending”, “failed” or “succeeded” 19 | final String? status; 20 | 21 | /// What you specified for the latest matched parser or one of the default failed cases above 22 | final String? statusMeaning; 23 | 24 | /// Message you specified for the latest matched parser 25 | final String? statusDescription; 26 | 27 | /// Unique identifier for the parser which matched, causing this transaction to update 28 | final int? matchedParserId; 29 | 30 | /// “ussd” or “sms” 31 | final String? messagetype; 32 | 33 | /// If SMS, the sender id from the parser form, null if USSD 34 | final String? messageSender; 35 | 36 | /// Regular expression you specified in the parser form 37 | final String? regex; 38 | 39 | /// The Home Network Identifier (MCC + MNC) of the SIM used 40 | final String? simHni; 41 | 42 | /// 0 for normal, 1 for debug, 2 for test 43 | final int? environment; 44 | 45 | /// Time user initiated transaction (Unix time) 46 | final int? requestTimestamp; 47 | 48 | /// Time at which the transaction last updated (SMS arrival or USSD finished) 49 | final int? updateTimestamp; 50 | 51 | /// (depreciated) Same as updated_timestamp 52 | final int? responseTimestamp; 53 | 54 | /// A HashMap object of all the extras you passed in using .extra(key, value) 55 | final Map? inputExtras; 56 | 57 | /// A HashMap object of all named groups parsed out of the response message based on your regex 58 | final Map? parsedVariables; 59 | 60 | /// Array of all USSD session messages in order encountered 61 | final List? sessionMessages; 62 | 63 | SmsParsed( 64 | {this.uuid, 65 | this.actionId, 66 | this.responseMessage, 67 | this.status, 68 | this.statusMeaning, 69 | this.statusDescription, 70 | this.matchedParserId, 71 | this.messagetype, 72 | this.messageSender, 73 | this.regex, 74 | this.simHni, 75 | this.environment, 76 | this.requestTimestamp, 77 | this.updateTimestamp, 78 | this.responseTimestamp, 79 | this.inputExtras, 80 | this.parsedVariables, 81 | this.sessionMessages}); 82 | @override 83 | Map toMap() { 84 | return { 85 | 'uuid': uuid, 86 | 'action_id': actionId, 87 | 'response_message': responseMessage, 88 | 'status': status, 89 | 'status_meaning': statusMeaning, 90 | 'status_description': statusDescription, 91 | 'matched_parser_id': matchedParserId, 92 | 'messagetype': messagetype, 93 | 'message_sender': messageSender, 94 | 'regex': regex, 95 | 'sim_hni': simHni, 96 | 'environment': environment, 97 | 'request_timestamp': requestTimestamp, 98 | 'update_timestamp': updateTimestamp, 99 | 'response_timestamp': responseTimestamp, 100 | 'input_extras': inputExtras, 101 | 'parsed_variables': parsedVariables, 102 | 'session_messages': sessionMessages, 103 | }; 104 | } 105 | 106 | factory SmsParsed.fromMap(Map json) { 107 | return SmsParsed( 108 | uuid: json['uuid'] as String?, 109 | sessionMessages: json['session_messages'] as List?, 110 | inputExtras: json['input_extras'] as Map?, 111 | parsedVariables: json['parsed_variables'] as Map?, 112 | responseTimestamp: json['response_timestamp'] as int?, 113 | updateTimestamp: json['update_timestamp'] as int?, 114 | requestTimestamp: json['request_timestamp'] as int?, 115 | environment: json['environment'] as int?, 116 | simHni: json['sim_hni'] as String?, 117 | regex: json['regex'] as String?, 118 | messageSender: json['message_sender'] as String?, 119 | messagetype: json['messagetype'] as String?, 120 | matchedParserId: json['matched_parser_id'] as int?, 121 | statusDescription: json['status_description'] as String?, 122 | statusMeaning: json['status_meaning'] as String?, 123 | actionId: json['action_id'] as String?, 124 | responseMessage: json['response_message'] as String?, 125 | status: json['status'] as String?, 126 | ); 127 | } 128 | } 129 | 130 | /// when ussd session run succesfully 131 | class UssdSucceeded extends TransactionState { 132 | /// Unique Identifier for the transaction 133 | final String? uuid; 134 | 135 | /// The action id from out supported operators page 136 | final String? actionId; 137 | 138 | /// Full message used for parsing 139 | final String? responseMessage; 140 | 141 | UssdSucceeded({this.uuid, this.actionId, this.responseMessage}); 142 | @override 143 | Map toMap() { 144 | return { 145 | 'uuid': uuid, 146 | 'action_id': actionId, 147 | 'response_message': responseMessage, 148 | }; 149 | } 150 | 151 | factory UssdSucceeded.fromMap(Map json) { 152 | return UssdSucceeded( 153 | uuid: json['uuid'] as String?, 154 | actionId: json['action_id'] as String?, 155 | responseMessage: json['response_message'] as String?, 156 | ); 157 | } 158 | } 159 | 160 | /// when the ussd code failed; this can be caused by user 161 | /// dissmiss or request refuse 162 | class UssdFailed extends TransactionState { 163 | /// error message if ussd call failed 164 | final String? errorMessage; 165 | 166 | UssdFailed({this.errorMessage}); 167 | 168 | factory UssdFailed.fromMap(Map json) { 169 | return UssdFailed(errorMessage: json["errorMessage"]); 170 | } 171 | 172 | @override 173 | Map toMap() { 174 | return {"errorMessage": errorMessage}; 175 | } 176 | } 177 | 178 | class UssdLoading extends TransactionState { 179 | @override 180 | Map toMap() { 181 | return {}; 182 | } 183 | } 184 | 185 | 186 | class EmptyState extends TransactionState { 187 | @override 188 | Map toMap() { 189 | return {}; 190 | } 191 | } -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.3.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.18.0" 44 | fake_async: 45 | dependency: transitive 46 | description: 47 | name: fake_async 48 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.3.1" 52 | flutter: 53 | dependency: "direct main" 54 | description: flutter 55 | source: sdk 56 | version: "0.0.0" 57 | flutter_lints: 58 | dependency: "direct dev" 59 | description: 60 | name: flutter_lints 61 | sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 62 | url: "https://pub.dev" 63 | source: hosted 64 | version: "2.0.3" 65 | flutter_test: 66 | dependency: "direct dev" 67 | description: flutter 68 | source: sdk 69 | version: "0.0.0" 70 | leak_tracker: 71 | dependency: transitive 72 | description: 73 | name: leak_tracker 74 | sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" 75 | url: "https://pub.dev" 76 | source: hosted 77 | version: "10.0.0" 78 | leak_tracker_flutter_testing: 79 | dependency: transitive 80 | description: 81 | name: leak_tracker_flutter_testing 82 | sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 83 | url: "https://pub.dev" 84 | source: hosted 85 | version: "2.0.1" 86 | leak_tracker_testing: 87 | dependency: transitive 88 | description: 89 | name: leak_tracker_testing 90 | sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 91 | url: "https://pub.dev" 92 | source: hosted 93 | version: "2.0.1" 94 | lints: 95 | dependency: transitive 96 | description: 97 | name: lints 98 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 99 | url: "https://pub.dev" 100 | source: hosted 101 | version: "2.1.1" 102 | matcher: 103 | dependency: transitive 104 | description: 105 | name: matcher 106 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 107 | url: "https://pub.dev" 108 | source: hosted 109 | version: "0.12.16+1" 110 | material_color_utilities: 111 | dependency: transitive 112 | description: 113 | name: material_color_utilities 114 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" 115 | url: "https://pub.dev" 116 | source: hosted 117 | version: "0.8.0" 118 | meta: 119 | dependency: transitive 120 | description: 121 | name: meta 122 | sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 123 | url: "https://pub.dev" 124 | source: hosted 125 | version: "1.11.0" 126 | path: 127 | dependency: transitive 128 | description: 129 | name: path 130 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 131 | url: "https://pub.dev" 132 | source: hosted 133 | version: "1.9.0" 134 | plugin_platform_interface: 135 | dependency: "direct main" 136 | description: 137 | name: plugin_platform_interface 138 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 139 | url: "https://pub.dev" 140 | source: hosted 141 | version: "2.1.8" 142 | sky_engine: 143 | dependency: transitive 144 | description: flutter 145 | source: sdk 146 | version: "0.0.99" 147 | source_span: 148 | dependency: transitive 149 | description: 150 | name: source_span 151 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 152 | url: "https://pub.dev" 153 | source: hosted 154 | version: "1.10.0" 155 | stack_trace: 156 | dependency: transitive 157 | description: 158 | name: stack_trace 159 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 160 | url: "https://pub.dev" 161 | source: hosted 162 | version: "1.11.1" 163 | stream_channel: 164 | dependency: transitive 165 | description: 166 | name: stream_channel 167 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 168 | url: "https://pub.dev" 169 | source: hosted 170 | version: "2.1.2" 171 | string_scanner: 172 | dependency: transitive 173 | description: 174 | name: string_scanner 175 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "1.2.0" 179 | term_glyph: 180 | dependency: transitive 181 | description: 182 | name: term_glyph 183 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 184 | url: "https://pub.dev" 185 | source: hosted 186 | version: "1.2.1" 187 | test_api: 188 | dependency: transitive 189 | description: 190 | name: test_api 191 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" 192 | url: "https://pub.dev" 193 | source: hosted 194 | version: "0.6.1" 195 | vector_math: 196 | dependency: transitive 197 | description: 198 | name: vector_math 199 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 200 | url: "https://pub.dev" 201 | source: hosted 202 | version: "2.1.4" 203 | vm_service: 204 | dependency: transitive 205 | description: 206 | name: vm_service 207 | sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 208 | url: "https://pub.dev" 209 | source: hosted 210 | version: "13.0.0" 211 | sdks: 212 | dart: ">=3.2.0-0 <4.0.0" 213 | flutter: ">=3.3.0" 214 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: hover_ussd 2 | description: A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. 3 | version: 2.0.0 4 | 5 | 6 | 7 | homepage: https://github.com/lucdotdev/hover_ussd 8 | 9 | environment: 10 | sdk: '>=3.1.3 <4.0.0' 11 | flutter: '>=3.3.0' 12 | 13 | dependencies: 14 | flutter: 15 | sdk: flutter 16 | plugin_platform_interface: ^2.0.2 17 | 18 | dev_dependencies: 19 | flutter_test: 20 | sdk: flutter 21 | flutter_lints: ^2.0.0 22 | 23 | # For information on the generic Dart part of this file, see the 24 | # following page: https://dart.dev/tools/pub/pubspec 25 | 26 | # The following section is specific to Flutter packages. 27 | flutter: 28 | # This section identifies this Flutter project as a plugin project. 29 | # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) 30 | # which should be registered in the plugin registry. This is required for 31 | # using method channels. 32 | # The Android 'package' specifies package in which the registered class is. 33 | # This is required for using method channels on Android. 34 | # The 'ffiPlugin' specifies that native code should be built and bundled. 35 | # This is required for using `dart:ffi`. 36 | # All these are used by the tooling to maintain consistency when 37 | # adding or updating assets for this project. 38 | plugin: 39 | platforms: 40 | android: 41 | package: com.lucdotdev.hover_ussd 42 | pluginClass: HoverUssdPlugin 43 | 44 | # To add assets to your plugin package, add an assets section, like this: 45 | # assets: 46 | # - images/a_dot_burr.jpeg 47 | # - images/a_dot_ham.jpeg 48 | # 49 | # For details regarding assets in packages, see 50 | # https://flutter.dev/assets-and-images/#from-packages 51 | # 52 | # An image asset can refer to one or more resolution-specific "variants", see 53 | # https://flutter.dev/assets-and-images/#resolution-aware 54 | 55 | # To add custom fonts to your plugin package, add a fonts section here, 56 | # in this "flutter" section. Each entry in this list should have a 57 | # "family" key with the font family name, and a "fonts" key with a 58 | # list giving the asset and other descriptors for the font. For 59 | # example: 60 | # fonts: 61 | # - family: Schyler 62 | # fonts: 63 | # - asset: fonts/Schyler-Regular.ttf 64 | # - asset: fonts/Schyler-Italic.ttf 65 | # style: italic 66 | # - family: Trajan Pro 67 | # fonts: 68 | # - asset: fonts/TrajanPro.ttf 69 | # - asset: fonts/TrajanPro_Bold.ttf 70 | # weight: 700 71 | # 72 | # For details regarding fonts in packages, see 73 | # https://flutter.dev/custom-fonts/#from-packages 74 | 75 | -------------------------------------------------------------------------------- /test/hover_ussd_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:hover_ussd/hover_ussd.dart'; 3 | 4 | import 'package:plugin_platform_interface/plugin_platform_interface.dart'; 5 | 6 | class MockHoverUssdPlatform 7 | with MockPlatformInterfaceMixin 8 | { 9 | 10 | } 11 | 12 | void main() { 13 | 14 | 15 | test('getPlatformVersion', () async { 16 | HoverUssd hoverUssdPlugin = HoverUssd(); 17 | ; 18 | }); 19 | } 20 | --------------------------------------------------------------------------------