├── .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 |
6 |
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 |
5 |
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 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | {
43 | "customColor": "",
44 | "associatedIndex": 0
45 | }
46 |
47 |
48 |
49 |
50 |
51 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | 1596904412123
80 |
81 |
82 | 1596904412123
83 |
84 |
85 |
86 | 1715466064852
87 |
88 |
89 |
90 | 1715466064852
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
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 | 
4 | [](https://pub.dartlang.org/packages/hover_ussd)
5 | [](https://github.com/lucdotdev/hover_ussd)
6 | [](https://flutter.dev/docs/development/data-and-backend/state-mgmt/options#bloc--rx)
7 | [](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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/android/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
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