From 87cf82ffad94d81dba4c3c0729e119f6f64e5aa7 Mon Sep 17 00:00:00 2001 From: luc-dotcom Date: Fri, 14 Aug 2020 12:52:38 +0200 Subject: [PATCH 01/36] [v0.0.2] changing license, adding logn description --- LICENSE | 26 +++++++++++++++++--------- example/pubspec.lock | 2 +- pubspec.yaml | 7 ++++--- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/LICENSE b/LICENSE index 5e79839..f4d84c8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,11 +1,19 @@ -Licensed under the Apache License, Version 2.0 (the "License"); you -may not use this file except in compliance with the License. You may -obtain a copy of the License at +Copyright 2019 Luc Mwansa -http://www.apache.org/licenses/LICENSE-2.0 +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied. See the License for the specific language governing permissions -and limitations under the License. \ No newline at end of file +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index bd59f4b..3514234 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -73,7 +73,7 @@ packages: path: ".." relative: true source: path - version: "0.0.1" + version: "0.0.2" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8961222..c5ec574 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,9 @@ name: hover_ussd -description: this is flutter integration of usehover.com sdk -version: 0.0.1 +description: A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. +version: 0.0.2 author: lucdotdev -homepage: + +homepage: https://github.com/lucdotdev/hover_ussd environment: sdk: ">=2.7.0 <3.0.0" From ade5c440cb9ad4932decca4ae910ad73773b5acc Mon Sep 17 00:00:00 2001 From: luc-dotcom Date: Fri, 14 Aug 2020 12:53:23 +0200 Subject: [PATCH 02/36] [v0.0.2] . --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index f4d84c8..1678cbf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2019 Luc Mwansa +Copyright 2020 Luc Mwansa Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 9844c9ca2da4baf5fb28cb9f756231894d00c7eb Mon Sep 17 00:00:00 2001 From: lucdotdev <55983266+lucdotdev@users.noreply.github.com> Date: Fri, 14 Aug 2020 13:33:31 +0200 Subject: [PATCH 03/36] [v0.0.2] update readme --- README.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e828aac..0355ca6 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,77 @@ [![Flutter Website](https://img.shields.io/badge/flutter-website-deepskyblue.svg)](https://flutter.dev/docs/development/data-and-backend/state-mgmt/options#bloc--rx) [![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) -A new flutter plugin project. +A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. +**android only** ## Getting Started -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/developing-packages/), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. +* Adding The hover api key refert to documentation at [docs.usehover.com](https://docs.usehover.com/) -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +```xml + +``` +## Usage +* Initialize the plugin in main method +```dart +void main() { + WidgetsFlutterBinding.ensureInitialized(); + HoverUssd().initialize(); + runApp(MyApp()); +} +``` +* Start a transaction +```dart +import 'package:hover_ussd/hover_ussd.dart'; +... +final HoverUssd _hoverUssd = HoverUssd(); +///Begin transaction +void send(){ + _hoverUssd.sendUssd("c6e45e62", {"price": "4000"}); +} + +///Listen for transaction status + _hoverUssd.onTransactiontateChanged.listen((event) { + // Do something with new state + if (event == TransactionState.succesfull) { + print("succesfull"); + } else if (event == TransactionState.waiting) { + print("pending"); + } else if (event == TransactionState.failed) { + print('failed'); + } + }); +///You can listen with StreamBuilder to update ui + StreamBuilder( + stream: _hoverUssd.onTransactiontateChanged, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.data == TransactionState.succesfull) { + return Text("succesfull"); + } else if (snapshot.data == TransactionState.waiting) { + return Text("pending"); + } else if (snapshot.data == TransactionState.failed) { + return Text("failed"); + } + return Text("no transaction"); + }, +); + +``` +## Features + - [x] start a transaction + - [x] listen for result + - [ ] customization + - [ ] translation + +## important + + * **support only basic feature** + * **always in developpement** + * **this isn't a officialy plugin** + +## maintainers +- [Lucdotdev](https://twitter.com/lucdotdev) + From 5faa14d39f20a09ab0ce2f93240664b6d5232193 Mon Sep 17 00:00:00 2001 From: lucdotdev <55983266+lucdotdev@users.noreply.github.com> Date: Fri, 14 Aug 2020 13:34:32 +0200 Subject: [PATCH 04/36] [v0.0.2] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0355ca6..01144a1 100644 --- a/README.md +++ b/README.md @@ -72,12 +72,12 @@ void send(){ - [ ] customization - [ ] translation -## important +## Important * **support only basic feature** * **always in developpement** * **this isn't a officialy plugin** -## maintainers +## Maintainers - [Lucdotdev](https://twitter.com/lucdotdev) From f01386b4073f5cc53d2e5e37be4a545a3b5153cd Mon Sep 17 00:00:00 2001 From: luc-dotcom Date: Fri, 14 Aug 2020 13:35:42 +0200 Subject: [PATCH 05/36] [v0.0.2] improve the package --- lib/hover_ussd.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart index 74338b0..a7d640b 100644 --- a/lib/hover_ussd.dart +++ b/lib/hover_ussd.dart @@ -29,7 +29,7 @@ class HoverUssd { await _methodChannel.invokeMethod( "hoverStartTransaction", {"action_id": actionId, "extras": extras}); - Stream onTransactiontateChanged() { + Stream get onTransactiontateChanged { if (_onTransactionStateChanged == null) { _onTransactionStateChanged = _eventChannel .receiveBroadcastStream() From e077cb0abce09d6247722665e5f987e6261577a8 Mon Sep 17 00:00:00 2001 From: luc-dotcom Date: Fri, 14 Aug 2020 13:39:25 +0200 Subject: [PATCH 06/36] [v0.0.2] update example --- example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index cd4e463..d2ae52f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -33,7 +33,7 @@ class _MyAppState extends State { child: Text("Start Trasaction"), ), StreamBuilder( - stream: _hoverUssd.onTransactiontateChanged(), + stream: _hoverUssd.onTransactiontateChanged, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.data == TransactionState.succesfull) { return Text("succesfull"); From e0c138a298a5f06fd9c60c746a090fb6ea4d80ca Mon Sep 17 00:00:00 2001 From: luc-dotcom Date: Fri, 14 Aug 2020 13:40:50 +0200 Subject: [PATCH 07/36] [v0.0.2] update readme --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9a73e1..c2c91f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,5 @@ +## 0.0.2 +* improve performance +* update readme ## 0.0.1 * Initial release From 92c04ca6b7b0d4dcbd44103e306fa2dbbda0e6ec Mon Sep 17 00:00:00 2001 From: lucdotdev <55983266+lucdotdev@users.noreply.github.com> Date: Fri, 14 Aug 2020 13:44:06 +0200 Subject: [PATCH 08/36] [v0.0.2] update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 01144a1..db33b58 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ final HoverUssd _hoverUssd = HoverUssd(); ///Begin transaction void send(){ + ///First param @String [action_id] + ///Second param @ Map [step_variables] _hoverUssd.sendUssd("c6e45e62", {"price": "4000"}); } From ad93e65cdf089c84c15f34948924d838c4459efc Mon Sep 17 00:00:00 2001 From: luc-dotcom Date: Fri, 14 Aug 2020 13:46:20 +0200 Subject: [PATCH 09/36] [v0.0.2] adding illustration --- docs/hover.png | Bin 0 -> 167128 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/hover.png diff --git a/docs/hover.png b/docs/hover.png new file mode 100644 index 0000000000000000000000000000000000000000..30b4b8eff3314acb2d447157e9ab9806ee1dd366 GIT binary patch literal 167128 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�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ξZ6 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 J2=c7=pxn+cz|F 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℘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 Date: Fri, 14 Aug 2020 13:50:10 +0200 Subject: [PATCH 10/36] [v0.0.2] adding illustration --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index db33b58..7b3d3bf 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ [![Flutter Website](https://img.shields.io/badge/flutter-website-deepskyblue.svg)](https://flutter.dev/docs/development/data-and-backend/state-mgmt/options#bloc--rx) [![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) +drawing + A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. **android only** From 7521c05c740a7c2a02811dc2fd9d70230f418dd9 Mon Sep 17 00:00:00 2001 From: lucdotdev <55983266+lucdotdev@users.noreply.github.com> Date: Fri, 14 Aug 2020 13:55:18 +0200 Subject: [PATCH 11/36] [v0.0.2] update readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 7b3d3bf..11c552f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ ![build](https://github.com/lucdotdev/hover_ussd/workflows/build/badge.svg) [![Pub](https://img.shields.io/pub/v/hover_ussd)](https://pub.dartlang.org/packages/hover_ussd) [![Star on GitHub](https://img.shields.io/github/stars/lucdotdev/hover_ussd)](https://github.com/lucdotdev/hover_ussd) -[![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://github.com/tenhobi/effective_dart) [![Flutter Website](https://img.shields.io/badge/flutter-website-deepskyblue.svg)](https://flutter.dev/docs/development/data-and-backend/state-mgmt/options#bloc--rx) [![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) From ca8158b88d9047deebde5751d49434e80f9f109e Mon Sep 17 00:00:00 2001 From: lucdotdev <55983266+lucdotdev@users.noreply.github.com> Date: Fri, 14 Aug 2020 14:29:45 +0200 Subject: [PATCH 12/36] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From cca92837dd7d486c62a13cee745262e5cc35653e Mon Sep 17 00:00:00 2001 From: luc-dotcom Date: Tue, 25 Aug 2020 23:10:17 +0200 Subject: [PATCH 13/36] [v0.0.2+1] code improvements according to #2 --- android/.idea/gradle.xml | 12 ++ android/.idea/modules.xml | 8 + android/.idea/vcs.xml | 6 + android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes android/gradlew | 160 ++++++++++++++++++ android/gradlew.bat | 90 ++++++++++ .../lucdotdev/hover_ussd/HoverUssdApi.java | 4 +- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 21 ++- .../hover_ussd/HoverUssdSmsReceiver.java | 10 +- example/lib/main.dart | 3 +- example/pubspec.lock | 2 +- lib/hover_ussd.dart | 5 +- pubspec.yaml | 2 +- 13 files changed, 300 insertions(+), 23 deletions(-) create mode 100644 android/.idea/modules.xml create mode 100644 android/.idea/vcs.xml create mode 100644 android/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/gradlew create mode 100644 android/gradlew.bat diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml index 3e3960b..178312e 100644 --- a/android/.idea/gradle.xml +++ b/android/.idea/gradle.xml @@ -1,4 +1,16 @@ + + + \ No newline at end of file diff --git a/android/.idea/modules.xml b/android/.idea/modules.xml new file mode 100644 index 0000000..9dddca5 --- /dev/null +++ b/android/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/android/.idea/vcs.xml b/android/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/android/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..13372aef5e24af05341d49695ee84e5f9b594659 GIT binary patch literal 53636 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ⅅ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 H-+%o-(+5aQ literal 0 HcmV?d00001 diff --git a/android/gradlew b/android/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index 634259e..39dc62e 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -2,7 +2,6 @@ import android.app.Activity; import android.content.BroadcastReceiver; -import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -14,7 +13,6 @@ import java.util.HashMap; import java.util.Map; -import io.flutter.Log; public class HoverUssdApi { @@ -32,7 +30,7 @@ public HoverUssdApi(Activity activity) { - public void sendUssd(String action_id, HashMap extra, HoverUssdSmsReceiver smsReceiver) { + public void sendUssd(String action_id, HashMap extra, BroadcastReceiver smsReceiver) { LocalBroadcastManager.getInstance(activity).registerReceiver(smsReceiver, new IntentFilter("com.lucdotdev.hover_ussd.SMS_MISS")); diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index 03287b7..d26ac16 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -3,8 +3,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.widget.ImageView; +import android.content.BroadcastReceiver; import android.widget.Toast; @@ -35,21 +34,26 @@ public class HoverUssdPlugin implements FlutterPlugin, MethodCallHandler, Activi private HoverUssdApi hoverUssdApi; private EventChannel eventChannel; private EventChannel.EventSink eventSink; - private HoverUssdSmsReceiver hoverUssdSmsReceiver; - + + private final BroadcastReceiver smsReceiver = new BroadcastReceiver(){ + @Override + public void onReceive(final Context context, final Intent i){ + Toast.makeText(activity, "Error: " + i.getStringExtra("status"), Toast.LENGTH_LONG).show(); + eventSink.success("failed"); + } + }; @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + new HoverUssdSmsReceiver(this); channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "hover_ussd"); eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "transaction_event"); eventChannel.setStreamHandler(this); channel.setMethodCallHandler(this); - hoverUssdSmsReceiver = new HoverUssdSmsReceiver(this); - } @Override @@ -57,7 +61,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { if (call.method.equals("hoverStartTransaction")) { hoverUssdApi = new HoverUssdApi(activity); - hoverUssdApi.sendUssd((String) call.argument("action_id"), (HashMap) call.argument("extras"), hoverUssdSmsReceiver); + hoverUssdApi.sendUssd((String) call.argument("action_id"), (HashMap) call.argument("extras"),smsReceiver); } else if(call.method.equals("hoverInitial")) { @@ -110,7 +114,7 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { } else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED) { - Toast.makeText(activity, "Error: " + data.getStringExtra("error"), Toast.LENGTH_LONG).show(); + Toast.makeText(activity, "Error: ", Toast.LENGTH_LONG).show(); eventSink.success("failed"); @@ -131,6 +135,7 @@ public void onCancel(Object arguments) { @Override public void onRecevedData(String msg) { + Toast.makeText(activity, "Error: " +msg, Toast.LENGTH_LONG).show(); eventSink.success(msg); } } diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdSmsReceiver.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdSmsReceiver.java index 4aa6aa7..cc1361d 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdSmsReceiver.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdSmsReceiver.java @@ -3,13 +3,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.util.Log; -import com.hover.sdk.transactions.Transaction; - -import io.flutter.plugin.common.EventChannel; - public final class HoverUssdSmsReceiver extends BroadcastReceiver{ private HoverUssdReceiverInterface hoverUssdReceiverInterface; @@ -18,8 +13,9 @@ public HoverUssdSmsReceiver(HoverUssdReceiverInterface hoverUssdReceiverInterfac } @Override public void onReceive(Context context, Intent intent) { - String t = Transaction.SUCCEEDED; - hoverUssdReceiverInterface.onRecevedData(t); + String t = intent.getStringExtra("status"); + assert t != null; + hoverUssdReceiverInterface.onRecevedData(t.toLowerCase()); } public interface HoverUssdReceiverInterface{ diff --git a/example/lib/main.dart b/example/lib/main.dart index d2ae52f..7f262ca 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -28,7 +28,8 @@ class _MyAppState extends State { children: [ FlatButton( onPressed: () { - _hoverUssd.sendUssd("c6e45e62", {"price": "4000"}); + _hoverUssd.sendUssd( + actionId: "c6e45e62", extras: {"price": "4000"}); }, child: Text("Start Trasaction"), ), diff --git a/example/pubspec.lock b/example/pubspec.lock index 3514234..add5612 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -73,7 +73,7 @@ packages: path: ".." relative: true source: path - version: "0.0.2" + version: "0.0.2+1" matcher: dependency: transitive description: diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart index a7d640b..8832a4b 100644 --- a/lib/hover_ussd.dart +++ b/lib/hover_ussd.dart @@ -25,7 +25,8 @@ class HoverUssd { Stream _onTransactionStateChanged; - Future sendUssd(String actionId, Map extras) async => + Future sendUssd( + {@required String actionId, Map extras}) async => await _methodChannel.invokeMethod( "hoverStartTransaction", {"action_id": actionId, "extras": extras}); @@ -38,7 +39,7 @@ class HoverUssd { return _onTransactionStateChanged; } - void initialize() async { + Future initialize() async { await _methodChannel.invokeMethod("hoverInitial"); } diff --git a/pubspec.yaml b/pubspec.yaml index c5ec574..8d1d927 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: hover_ussd description: A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. -version: 0.0.2 +version: 0.0.2+1 author: lucdotdev homepage: https://github.com/lucdotdev/hover_ussd From ae61f4cb454745f0fdc48676ee5a3b7217d2f162 Mon Sep 17 00:00:00 2001 From: luc-dotcom Date: Tue, 25 Aug 2020 23:13:29 +0200 Subject: [PATCH 14/36] [v0.0.2+1] preparing for publish --- CHANGELOG.md | 2 ++ pubspec.yaml | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c91f4..ed6afc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +##0.0.2+1 +* improve code ## 0.0.2 * improve performance * update readme diff --git a/pubspec.yaml b/pubspec.yaml index 8d1d927..8a18a21 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,6 @@ name: hover_ussd description: A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. version: 0.0.2+1 -author: lucdotdev homepage: https://github.com/lucdotdev/hover_ussd From ad46aeac0d70537f36264151ea7a670559d4f747 Mon Sep 17 00:00:00 2001 From: luc-dotcom Date: Wed, 26 Aug 2020 09:07:38 +0200 Subject: [PATCH 15/36] [v0.0.2+2] update readme --- CHANGELOG.md | 4 +++- README.md | 4 +--- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed6afc9..3236482 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -##0.0.2+1 +## 0.0.2+2 +* update readme +## 0.0.2+1 * improve code ## 0.0.2 * improve performance diff --git a/README.md b/README.md index 11c552f..d74418a 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,7 @@ final HoverUssd _hoverUssd = HoverUssd(); ///Begin transaction void send(){ - ///First param @String [action_id] - ///Second param @ Map [step_variables] - _hoverUssd.sendUssd("c6e45e62", {"price": "4000"}); + _hoverUssd.sendUssd(actionId: "c6e45e62", extras: {"price": "4000"});; } ///Listen for transaction status diff --git a/example/pubspec.lock b/example/pubspec.lock index add5612..0dfccab 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -73,7 +73,7 @@ packages: path: ".." relative: true source: path - version: "0.0.2+1" + version: "0.0.2+2" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8a18a21..7c75e9a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: hover_ussd description: A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. -version: 0.0.2+1 +version: 0.0.2+2 homepage: https://github.com/lucdotdev/hover_ussd From ef28cc9dd4019341ab6a14710a361884e6bd2ec0 Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Fri, 3 Sep 2021 14:29:40 +0200 Subject: [PATCH 16/36] v 1.0.0 --- CHANGELOG.md | 8 +- README.md | 93 ++++++---- android/.project | 34 ++++ android/src/main/AndroidManifest.xml | 12 +- .../lucdotdev/hover_ussd/HoverUssdApi.java | 47 +++-- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 175 +++++++++--------- .../hover_ussd/HoverUssdSmsReceiver.java | 24 --- example/android/.project | 28 +++ example/android/app/.project | 34 ++++ example/ios/Flutter/Generated.xcconfig | 13 ++ .../ios/Flutter/flutter_export_environment.sh | 13 ++ .../ios/Runner/GeneratedPluginRegistrant.h | 19 ++ .../ios/Runner/GeneratedPluginRegistrant.m | 14 ++ example/lib/main.dart | 9 +- example/pubspec.lock | 42 ++--- example/pubspec.yaml | 3 +- lib/hover_ussd.dart | 71 +++---- pubspec.lock | 40 ++-- pubspec.yaml | 7 +- 19 files changed, 416 insertions(+), 270 deletions(-) create mode 100644 android/.project delete mode 100644 android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdSmsReceiver.java create mode 100644 example/android/.project create mode 100644 example/android/app/.project create mode 100644 example/ios/Flutter/Generated.xcconfig create mode 100644 example/ios/Flutter/flutter_export_environment.sh create mode 100644 example/ios/Runner/GeneratedPluginRegistrant.h create mode 100644 example/ios/Runner/GeneratedPluginRegistrant.m diff --git a/CHANGELOG.md b/CHANGELOG.md index 3236482..941ed23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.0.0 +*change from hover standar to hover noSms cc : +*no need to calling HoverUssd().initialise() from main() +*some api improvement **onTransactiontateChanged** changed to **getUssdTransactionState** +*You can now customize the hover the by passing branding or drawable to the HoverUssd Contruction +*You can also provide the theme of hover ussd ## 0.0.2+2 * update readme ## 0.0.2+1 @@ -6,4 +12,4 @@ * improve performance * update readme ## 0.0.1 -* Initial release +* Initial release \ No newline at end of file diff --git a/README.md b/README.md index d74418a..85771ca 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ [![Flutter Website](https://img.shields.io/badge/flutter-website-deepskyblue.svg)](https://flutter.dev/docs/development/data-and-backend/state-mgmt/options#bloc--rx) [![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) -drawing + +© image by Francis Mwakitumbula + A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. **android only** @@ -21,64 +23,77 @@ A flutter plugin to make payments by usehover.com ussd gateway using Android Int android:value=""/> ``` ## Usage -* Initialize the plugin in main method -```dart +* Example +```dart +import 'package:flutter/material.dart'; + +import 'package:hover_ussd/hover_ussd.dart'; + void main() { WidgetsFlutterBinding.ensureInitialized(); - HoverUssd().initialize(); + HoverUssd.initialize(); runApp(MyApp()); } -``` -* Start a transaction -```dart -import 'package:hover_ussd/hover_ussd.dart'; -... -final HoverUssd _hoverUssd = HoverUssd(); -///Begin transaction -void send(){ - _hoverUssd.sendUssd(actionId: "c6e45e62", extras: {"price": "4000"});; +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); } -///Listen for transaction status - _hoverUssd.onTransactiontateChanged.listen((event) { - // Do something with new state - if (event == TransactionState.succesfull) { - print("succesfull"); - } else if (event == TransactionState.waiting) { - print("pending"); - } else if (event == TransactionState.failed) { - print('failed'); - } - }); -///You can listen with StreamBuilder to update ui - StreamBuilder( - stream: _hoverUssd.onTransactiontateChanged, - builder: (BuildContext context, AsyncSnapshot snapshot) { +class _MyAppState extends State { + final HoverUssd _hoverUssd = HoverUssd(); + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Row( + children: [ + FlatButton( + onPressed: () { + _hoverUssd.sendUssd( + actionId: "c6e45e62", extras: {"price": "4000"}); + }, + child: Text("Start Trasaction"), + ), + StreamBuilder( + stream: _hoverUssd.getUssdTransactionState, + builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.data == TransactionState.succesfull) { return Text("succesfull"); - } else if (snapshot.data == TransactionState.waiting) { - return Text("pending"); + } else if (snapshot.data == + TransactionState.actionDowaloadFailed) { + return Text("action download failed"); } else if (snapshot.data == TransactionState.failed) { return Text("failed"); } - return Text("no transaction"); - }, -); + return Text("no transaction"); + }, + ), + ], + ), + ), + ), + ); + } +} ``` ## Features - [x] start a transaction - [x] listen for result - - [ ] customization + - [x] customization - [ ] translation ## Important - * **support only basic feature** - * **always in developpement** - * **this isn't a officialy plugin** + + * **Production ready** + * **This is a unofficial plugin** ## Maintainers -- [Lucdotdev](https://twitter.com/lucdotdev) - +- [lucdotdev](mailto:lucdotdev@gmail.com) \ No newline at end of file diff --git a/android/.project b/android/.project new file mode 100644 index 0000000..32b873b --- /dev/null +++ b/android/.project @@ -0,0 +1,34 @@ + + + hover_ussd + Project android created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + + + + 1630668570936 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 594fba6..a811528 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -2,15 +2,5 @@ xmlns:tools="http://schemas.android.com/tools" package="com.lucdotdev.hover_ussd"> - - - - - - - + diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index 39dc62e..dda3b95 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -1,12 +1,11 @@ package com.lucdotdev.hover_ussd; - import android.app.Activity; -import android.content.BroadcastReceiver; + +import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import com.hover.sdk.api.HoverParameters; @@ -23,31 +22,27 @@ public HoverUssdApi(Activity activity) { this.activity = activity; } - ///This Start Listennig SMS before the begin of Transaction - /// - /// - - - - - public void sendUssd(String action_id, HashMap extra, BroadcastReceiver smsReceiver) { + public void sendUssd(String action_id, HashMap extra, String theme) { + ///Initialize @HoverBuilder + final HoverParameters.Builder builder = new HoverParameters.Builder(activity).request(action_id); - LocalBroadcastManager.getInstance(activity).registerReceiver(smsReceiver, new IntentFilter("com.lucdotdev.hover_ussd.SMS_MISS")); - - ///Initialize @HoverBuilder - final HoverParameters.Builder builder = new HoverParameters.Builder(activity).request(action_id); - - ///If there are action with variables - /// - if (!extra.isEmpty()) { - for (Map.Entry entry : extra.entrySet()) { - builder.extra(entry.getKey(), entry.getValue()); - } + ///If there are action with variables + /// + if (!extra.isEmpty()) { + for (Map.Entry entry : extra.entrySet()) { + builder.extra(entry.getKey(), entry.getValue()); } - - Intent buildIntent = builder.buildIntent(); - activity.startActivityForResult(buildIntent, 0); } + if (theme != null) { + Context context = activity.getApplicationContext(); + int id = context.getResources().getIdentifier(theme, "style", context.getPackageName()); + builder.style(id); + } + Intent buildIntent = builder.buildIntent(); + activity.startActivityForResult(buildIntent, 0); } + + +} \ No newline at end of file diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index d26ac16..c4f09e9 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -3,16 +3,20 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.content.BroadcastReceiver; -import android.widget.Toast; +import android.os.Build; import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import com.hover.sdk.actions.HoverAction; import com.hover.sdk.api.Hover; +import java.util.ArrayList; import java.util.HashMap; +import java.util.Objects; +import io.flutter.Log; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.EventChannel; @@ -23,119 +27,114 @@ import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.plugin.common.PluginRegistry; -/** HoverUssdPlugin */ -public class HoverUssdPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware ,PluginRegistry.ActivityResultListener, EventChannel.StreamHandler, HoverUssdSmsReceiver.HoverUssdReceiverInterface { +/** + * HoverUssdPlugin + */ +public class HoverUssdPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener, EventChannel.StreamHandler { - private MethodChannel channel; - private Activity activity; + private MethodChannel channel; + private Activity activity; - private HoverUssdApi hoverUssdApi; - private EventChannel eventChannel; - private EventChannel.EventSink eventSink; - + private EventChannel eventChannel; + private EventChannel.EventSink eventSink; - private final BroadcastReceiver smsReceiver = new BroadcastReceiver(){ - @Override - public void onReceive(final Context context, final Intent i){ - Toast.makeText(activity, "Error: " + i.getStringExtra("status"), Toast.LENGTH_LONG).show(); - eventSink.success("failed"); - } - }; - - - @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - - new HoverUssdSmsReceiver(this); - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "hover_ussd"); - eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "transaction_event"); - eventChannel.setStreamHandler(this); - channel.setMethodCallHandler(this); - } - - @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { - if (call.method.equals("hoverStartTransaction")) { - hoverUssdApi = new HoverUssdApi(activity); - hoverUssdApi.sendUssd((String) call.argument("action_id"), (HashMap) call.argument("extras"),smsReceiver); + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - } else if(call.method.equals("hoverInitial")) { - Hover.initialize(activity.getApplicationContext()); - } else { - result.notImplemented(); + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "HoverUssdChannel"); + eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "TransactionEvent"); + eventChannel.setStreamHandler(this); + channel.setMethodCallHandler(this); } - } - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); - eventChannel.setStreamHandler(null); - } + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + HoverUssdApi hoverUssdApi = new HoverUssdApi(activity); + switch (call.method) { + case "HoverStartATransaction": + hoverUssdApi.sendUssd((String) call.argument("actionId"), + (HashMap) Objects.requireNonNull(call.argument("extras")), + (String) call.argument("theme")); + break; + case "Initialize": + + Hover.initialize(activity.getApplicationContext()); + if (call.argument("branding") != null && call.argument("logo") != null) { + Context context = activity.getApplicationContext(); + int id = context.getResources().getIdentifier((String) call.argument("logo"), "drawable", context.getPackageName()); + Hover.setBranding((String) call.argument("branding"), id, activity); + } + break; + default: + result.notImplemented(); + + } - @Override - public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { - activity = binding.getActivity(); - binding.addActivityResultListener(this); + } - } + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + channel.setMethodCallHandler(null); + eventChannel.setStreamHandler(null); + } - @Override - public void onDetachedFromActivityForConfigChanges() { - activity = null; - } + @Override + public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { + activity = binding.getActivity(); - @Override - public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { - activity = binding.getActivity(); - } + binding.addActivityResultListener(this); - @Override - public void onDetachedFromActivity() { - activity = null; - ///this help us to destroy the smsReceiver - // hoverUssdApi.destroySmsReceiver(); + } - } + @Override + public void onDetachedFromActivityForConfigChanges() { + activity = null; + } + @Override + public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { + activity = binding.getActivity(); + } - @Override - public boolean onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == 0 && resultCode == Activity.RESULT_OK) { + @Override + public void onDetachedFromActivity() { + activity = null; + } - Toast.makeText(activity, "Please wait for confirmation", Toast.LENGTH_LONG).show(); - eventSink.success("pending"); - return true; + @Override + public boolean onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == 0 && resultCode == Activity.RESULT_OK) { + Log.d("HOVER_MSG:", "The ussd action called succefully"); + eventSink.success("success"); - } else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED) { + return true; - Toast.makeText(activity, "Error: ", Toast.LENGTH_LONG).show(); + } else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED) { - eventSink.success("failed"); + Log.e("HOVER_ERROR:", "Hover ussd transaction error"); + eventSink.success("failed"); - return true; + return true; + } + return false; } - return false; - } - @Override - public void onListen(Object arguments, EventChannel.EventSink events) { - eventSink = events; - } + @Override + public void onListen(Object arguments, EventChannel.EventSink events) { + eventSink = events; + } - @Override - public void onCancel(Object arguments) { + @Override + public void onCancel(Object arguments) { + eventSink = null; + } - } - @Override - public void onRecevedData(String msg) { - Toast.makeText(activity, "Error: " +msg, Toast.LENGTH_LONG).show(); - eventSink.success(msg); - } -} +} \ No newline at end of file diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdSmsReceiver.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdSmsReceiver.java deleted file mode 100644 index cc1361d..0000000 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdSmsReceiver.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.lucdotdev.hover_ussd; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - - -public final class HoverUssdSmsReceiver extends BroadcastReceiver{ - private HoverUssdReceiverInterface hoverUssdReceiverInterface; - - public HoverUssdSmsReceiver(HoverUssdReceiverInterface hoverUssdReceiverInterface){ - this.hoverUssdReceiverInterface = hoverUssdReceiverInterface; - } - @Override - public void onReceive(Context context, Intent intent) { - String t = intent.getStringExtra("status"); - assert t != null; - hoverUssdReceiverInterface.onRecevedData(t.toLowerCase()); - } - - public interface HoverUssdReceiverInterface{ - void onRecevedData(String msg) ; - } -} diff --git a/example/android/.project b/example/android/.project new file mode 100644 index 0000000..c82e66c --- /dev/null +++ b/example/android/.project @@ -0,0 +1,28 @@ + + + android + Project android_ created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + + + 1630668570900 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/example/android/app/.project b/example/android/app/.project new file mode 100644 index 0000000..3fd00ca --- /dev/null +++ b/example/android/app/.project @@ -0,0 +1,34 @@ + + + app + Project app created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + + + + 1630668570914 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/example/ios/Flutter/Generated.xcconfig b/example/ios/Flutter/Generated.xcconfig new file mode 100644 index 0000000..08f6844 --- /dev/null +++ b/example/ios/Flutter/Generated.xcconfig @@ -0,0 +1,13 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=C:\flutter +FLUTTER_APPLICATION_PATH=C:\code\current\hover_ussd\example +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_TARGET=lib\main.dart +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=false +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.packages diff --git a/example/ios/Flutter/flutter_export_environment.sh b/example/ios/Flutter/flutter_export_environment.sh new file mode 100644 index 0000000..987b36b --- /dev/null +++ b/example/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\flutter" +export "FLUTTER_APPLICATION_PATH=C:\code\current\hover_ussd\example" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_TARGET=lib\main.dart" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=false" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.packages" diff --git a/example/ios/Runner/GeneratedPluginRegistrant.h b/example/ios/Runner/GeneratedPluginRegistrant.h new file mode 100644 index 0000000..7a89092 --- /dev/null +++ b/example/ios/Runner/GeneratedPluginRegistrant.h @@ -0,0 +1,19 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GeneratedPluginRegistrant_h +#define GeneratedPluginRegistrant_h + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GeneratedPluginRegistrant : NSObject ++ (void)registerWithRegistry:(NSObject*)registry; +@end + +NS_ASSUME_NONNULL_END +#endif /* GeneratedPluginRegistrant_h */ diff --git a/example/ios/Runner/GeneratedPluginRegistrant.m b/example/ios/Runner/GeneratedPluginRegistrant.m new file mode 100644 index 0000000..efe65ec --- /dev/null +++ b/example/ios/Runner/GeneratedPluginRegistrant.m @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#import "GeneratedPluginRegistrant.h" + +@implementation GeneratedPluginRegistrant + ++ (void)registerWithRegistry:(NSObject*)registry { +} + +@end diff --git a/example/lib/main.dart b/example/lib/main.dart index 7f262ca..43392d2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,7 +4,7 @@ import 'package:hover_ussd/hover_ussd.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); - HoverUssd().initialize(); + HoverUssd.initialize(); runApp(MyApp()); } @@ -34,12 +34,13 @@ class _MyAppState extends State { child: Text("Start Trasaction"), ), StreamBuilder( - stream: _hoverUssd.onTransactiontateChanged, + stream: _hoverUssd.getUssdTransactionState, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.data == TransactionState.succesfull) { return Text("succesfull"); - } else if (snapshot.data == TransactionState.waiting) { - return Text("pending"); + } else if (snapshot.data == + TransactionState.actionDowaloadFailed) { + return Text("action download failed"); } else if (snapshot.data == TransactionState.failed) { return Text("failed"); } diff --git a/example/pubspec.lock b/example/pubspec.lock index 0dfccab..2e129c6 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,42 +7,42 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety" + version: "2.8.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety" + version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.2" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety" + version: "1.3.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.2" + version: "1.15.0" cupertino_icons: dependency: "direct main" description: @@ -56,7 +56,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety" + version: "1.2.0" flutter: dependency: "direct main" description: flutter @@ -73,28 +73,28 @@ packages: path: ".." relative: true source: path - version: "0.0.2+2" + version: "1.0.0" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety" + version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.2" + version: "1.7.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety" + version: "1.8.0" sky_engine: dependency: transitive description: flutter @@ -106,56 +106,56 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety" + version: "1.8.1" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety" + version: "0.4.2" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.2" + version: "1.3.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.2" + version: "2.1.0" sdks: - dart: ">=2.10.0-0.0.dev <2.10.0" - flutter: ">=1.20.0 <2.0.0" + dart: ">=2.12.0 <3.0.0" + flutter: ">=1.20.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 52b2b49..ecd2757 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -6,7 +6,8 @@ description: Demonstrates how to use the hover_ussd plugin. publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' + dependencies: flutter: diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart index 8832a4b..6eb59bc 100644 --- a/lib/hover_ussd.dart +++ b/lib/hover_ussd.dart @@ -1,36 +1,48 @@ import 'dart:async'; - import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; -enum TransactionState { succesfull, waiting, failed } +enum TransactionState { + // when the ussd code run succesfully + + succesfull, + // when the ussd code failed; this can be caused by user + // dissmiss or request refuse + failed, + // this is when hover failed to dowload the list of your + // action , usualy when the app is not connected + actionDowaloadFailed +} class HoverUssd { - final MethodChannel _methodChannel; - final EventChannel _eventChannel; - - factory HoverUssd() { - if (_instance == null) { - final MethodChannel methodChannel = const MethodChannel('hover_ussd'); - final EventChannel eventChannel = const EventChannel('transaction_event'); - _instance = HoverUssd.private(methodChannel, eventChannel); - } - return _instance; - } + static const MethodChannel _methodChannel = MethodChannel('HoverUssdChannel'); + static const EventChannel _eventChannel = EventChannel('TransactionEvent'); - static HoverUssd _instance; + static Future initialize({String? branding, String? logo}) async { + await _methodChannel.invokeMethod("Initialize", + {"branding": branding ?? "Flutter App", "logo": logo ?? "ic_launcher"}); + } - @visibleForTesting - HoverUssd.private(this._methodChannel, this._eventChannel); + Stream? _onTransactionStateChanged; - Stream _onTransactionStateChanged; + // when you call send ussd, automatically the ussd procces begin + // and the permission is autaumatically handle + // this method came with 3 params + // @actionId is the id for the action you trying to call + // cc: https://docs.usehover.com/ussd + // @extras is the extras variable which came with action id , you can + // convigure tha on : https://www.usehover.com/ + // optional: if you want to customize the ussd theme then you can add a theme at + // your style.xml and then add the style string on @theme param + Future sendUssd( + {required String actionId, + Map? extras, + String? theme}) async => + await _methodChannel.invokeMethod("HoverStartATransaction", + {"actionId": actionId, "extras": extras ?? {}, "theme": theme}); - Future sendUssd( - {@required String actionId, Map extras}) async => - await _methodChannel.invokeMethod( - "hoverStartTransaction", {"action_id": actionId, "extras": extras}); + // this is for getting response of the current ussd session - Stream get onTransactiontateChanged { + Stream? get getUssdTransactionState { if (_onTransactionStateChanged == null) { _onTransactionStateChanged = _eventChannel .receiveBroadcastStream() @@ -39,19 +51,14 @@ class HoverUssd { return _onTransactionStateChanged; } - Future initialize() async { - await _methodChannel.invokeMethod("hoverInitial"); - } - - TransactionState _parseTransactionState(String state) { + TransactionState _parseTransactionState(String? state) { switch (state) { - case "succeeded": + case "success": return TransactionState.succesfull; - break; - case "pending": - return TransactionState.waiting; case "failed": return TransactionState.failed; + case "actionDownloadFailed": + return TransactionState.actionDowaloadFailed; default: throw ArgumentError('$state'); } diff --git a/pubspec.lock b/pubspec.lock index 42e0dd6..1ab9fcf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,49 +7,49 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety" + version: "2.8.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety" + version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.2" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety" + version: "1.3.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.2" + version: "1.15.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety" + version: "1.2.0" flutter: dependency: "direct main" description: flutter @@ -66,21 +66,21 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety" + version: "0.12.10" meta: dependency: "direct main" description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.2" + version: "1.7.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety" + version: "1.8.0" sky_engine: dependency: transitive description: flutter @@ -92,56 +92,56 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety" + version: "1.8.1" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety" + version: "0.4.2" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.2" + version: "1.3.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.2" + version: "2.1.0" sdks: - dart: ">=2.10.0-0.0.dev <2.10.0" - flutter: ">=1.20.0 <2.0.0" + dart: ">=2.12.0 <3.0.0" + flutter: ">=1.20.0" diff --git a/pubspec.yaml b/pubspec.yaml index 7c75e9a..b2ea127 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,12 +1,13 @@ name: hover_ussd description: A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. -version: 0.0.2+2 +version: 1.0.0 homepage: https://github.com/lucdotdev/hover_ussd environment: - sdk: ">=2.7.0 <3.0.0" - flutter: ">=1.20.0 <2.0.0" + sdk: '>=2.12.0 <3.0.0' + flutter: ">=1.20.0" + dependencies: flutter: From 8a452c977fcf31b746ef1cf2aafd0d293a769346 Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Wed, 10 Nov 2021 16:59:35 +0200 Subject: [PATCH 17/36] Candidate 1 --- .idea/workspace.xml | 23 +- CHANGELOG.md | 13 +- README.md | 71 +++-- android/.classpath | 6 + .../org.eclipse.buildship.core.prefs | 13 + android/build.gradle | 2 +- android/src/main/AndroidManifest.xml | 2 +- .../lucdotdev/hover_ussd/HoverUssdApi.java | 54 +++- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 173 ++++++++--- .../org.eclipse.buildship.core.prefs | 13 + example/android/app/.classpath | 6 + .../org.eclipse.buildship.core.prefs | 2 + .../android/app/src/main/AndroidManifest.xml | 5 +- .../app/src/main/res/values/styles.xml | 32 ++ example/lib/main.dart | 79 ++--- example/pubspec.lock | 2 +- lib/hover_ussd.dart | 273 +++++++++++++++--- pubspec.lock | 2 +- pubspec.yaml | 6 +- test/hover_ussd_test.dart | 1 - 20 files changed, 585 insertions(+), 193 deletions(-) create mode 100644 android/.classpath create mode 100644 android/.settings/org.eclipse.buildship.core.prefs create mode 100644 example/android/.settings/org.eclipse.buildship.core.prefs create mode 100644 example/android/app/.classpath create mode 100644 example/android/app/.settings/org.eclipse.buildship.core.prefs diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 438939d..9ad56c0 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,19 +2,18 @@ - - - - - - + + + + + - + @@ -25,8 +24,10 @@ + + @@ -43,9 +44,13 @@ - + - + + + + + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 941ed23..d627291 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ -## 1.0.0 -*change from hover standar to hover noSms cc : -*no need to calling HoverUssd().initialise() from main() -*some api improvement **onTransactiontateChanged** changed to **getUssdTransactionState** -*You can now customize the hover the by passing branding or drawable to the HoverUssd Contruction -*You can also provide the theme of hover ussd +## 2.0.0 +* revert to hover standart +## 1.0.0a +* change from hover standard to hover noSms +* You can now customize the hover the by passing branding or drawable to the HoverUssd Contruction +* You can also provide the theme of hover ussd +* ## 0.0.2+2 * update readme ## 0.0.2+1 diff --git a/README.md b/README.md index 85771ca..1562ef4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ © image by Francis Mwakitumbula -A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. +A flutter plugin implemanting usehover.com ussd gateway sdk using Android Intent and receiving the transaction information back in response. **android only** ## Getting Started @@ -22,25 +22,16 @@ A flutter plugin to make payments by usehover.com ussd gateway using Android Int android:name="com.hover.ApiKey" android:value=""/> ``` -## Usage -* Example -```dart -import 'package:flutter/material.dart'; - -import 'package:hover_ussd/hover_ussd.dart'; +## Basic Usage +```dart void main() { WidgetsFlutterBinding.ensureInitialized(); - HoverUssd.initialize(); + HoverUssd.initialize(branding: 'hover ussd example'); runApp(MyApp()); } -class MyApp extends StatefulWidget { - @override - _MyAppState createState() => _MyAppState(); -} - -class _MyAppState extends State { +class MyApp extends StatelessWidget { final HoverUssd _hoverUssd = HoverUssd(); @override @@ -48,7 +39,7 @@ class _MyAppState extends State { return MaterialApp( home: Scaffold( appBar: AppBar( - title: const Text('Plugin example app'), + title: const Text('hover ussd example'), ), body: Center( child: Row( @@ -56,22 +47,35 @@ class _MyAppState extends State { FlatButton( onPressed: () { _hoverUssd.sendUssd( - actionId: "c6e45e62", extras: {"price": "4000"}); + actionId: "c6e45e62", + extras: {"price": "4000"}, + theme: "myHoverTheme", + header: "transaction airtel", + showUserStepDescriptions: true); }, child: Text("Start Trasaction"), ), - StreamBuilder( + StreamBuilder( stream: _hoverUssd.getUssdTransactionState, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.data == TransactionState.succesfull) { - return Text("succesfull"); - } else if (snapshot.data == - TransactionState.actionDowaloadFailed) { - return Text("action download failed"); - } else if (snapshot.data == TransactionState.failed) { - return Text("failed"); + builder: (BuildContext context, snapshot) { + if (snapshot.data is SmsParsed) { + return Text( + "Sms parsed : \n" + snapshot.data!.toMap().toString()); + } + + if (snapshot.data is UssdSucceded) { + return Text("Ussd Succeded : \n" + + snapshot.data!.toMap().toString()); } - return Text("no transaction"); + if (snapshot.data is UssdLoading) { + return Text("loading..."); + } + if (snapshot.data is UssdFailed) { + return Text( + "Ussd Failed : \n" + snapshot.data!.toMap().toString()); + } + + return Text(""); }, ), ], @@ -81,19 +85,14 @@ class _MyAppState extends State { ); } } - ``` -## Features - - [x] start a transaction - - [x] listen for result - - [x] customization - - [ ] translation - -## Important - - * **Production ready** +## Customization + +## Important * **This is a unofficial plugin** +## Credit +* Thanks to the authors of useHover android sdk, this work based of it ## Maintainers - [lucdotdev](mailto:lucdotdev@gmail.com) \ No newline at end of file diff --git a/android/.classpath b/android/.classpath new file mode 100644 index 0000000..4a04201 --- /dev/null +++ b/android/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..2378e3a --- /dev/null +++ b/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir=../example/android +eclipse.preferences.version=1 +gradle.user.home= +java.home=C\:/Program Files/AdoptOpenJDK/jdk-11.0.10.9-hotspot +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/android/build.gradle b/android/build.gradle index e7a8d2f..f986d89 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -42,6 +42,6 @@ android { } dependencies { - implementation 'com.hover:android-sdk:1.6.3' + implementation 'com.hover:android-sdk:1.7.2' } } diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index a811528..9b9c0d5 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -2,5 +2,5 @@ xmlns:tools="http://schemas.android.com/tools" package="com.lucdotdev.hover_ussd"> - + diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index dda3b95..216324c 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -1,48 +1,74 @@ package com.lucdotdev.hover_ussd; -import android.app.Activity; + +import android.app.Activity; import android.content.Context; import android.content.Intent; - import com.hover.sdk.api.HoverParameters; import java.util.HashMap; import java.util.Map; +import io.flutter.plugin.common.EventChannel; + public class HoverUssdApi { private Activity activity; + private Context context; - public HoverUssdApi(Activity activity) { + + private EventChannel.EventSink eventSink; + + public HoverUssdApi(Activity activity, Context context,EventChannel.EventSink eventSink) { this.activity = activity; + this.eventSink = eventSink; + this.context = context; } - public void sendUssd(String action_id, HashMap extra, String theme) { - ///Initialize @HoverBuilder - final HoverParameters.Builder builder = new HoverParameters.Builder(activity).request(action_id); - - ///If there are action with variables - /// - if (!extra.isEmpty()) { - for (Map.Entry entry : extra.entrySet()) { - builder.extra(entry.getKey(), entry.getValue()); + public void sendUssd(String action_id, + HashMap extra, + String theme, + String header, + String initialProcessingMessage, + int finalMsgDisplayTime, + boolean showUserStepDescriptions) { + + final HoverParameters.Builder builder = new HoverParameters.Builder(context).request(action_id); + + if (extra != null) { + if (!extra.isEmpty()) { + for (Map.Entry entry : extra.entrySet()) { + builder.extra(entry.getKey(), entry.getValue()); + } } } + if (theme != null) { - Context context = activity.getApplicationContext(); int id = context.getResources().getIdentifier(theme, "style", context.getPackageName()); builder.style(id); + + } + if (header != null) { + builder.setHeader(header); } + if (initialProcessingMessage != null) { + builder.initialProcessingMessage(initialProcessingMessage); + } + + + builder.showUserStepDescriptions(showUserStepDescriptions); + builder.finalMsgDisplayTime(finalMsgDisplayTime); Intent buildIntent = builder.buildIntent(); + activity.startActivityForResult(buildIntent, 0); - } + } } \ No newline at end of file diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index c4f09e9..d268237 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -1,83 +1,63 @@ package com.lucdotdev.hover_ussd; import android.app.Activity; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.graphics.drawable.Drawable; import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; -import com.hover.sdk.actions.HoverAction; import com.hover.sdk.api.Hover; -import java.util.ArrayList; +import java.io.FileDescriptor; +import java.io.IOException; import java.util.HashMap; +import java.util.Map; import java.util.Objects; -import io.flutter.Log; import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.plugin.common.PluginRegistry; + /** * HoverUssdPlugin */ -public class HoverUssdPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener, EventChannel.StreamHandler { +public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener, EventChannel.StreamHandler { private MethodChannel channel; - private Activity activity; - - + Activity activity; + Context context; private EventChannel eventChannel; private EventChannel.EventSink eventSink; + private BroadcastReceiver smsReceiver; + private String intentNullAwareString(Intent intent, String name) { + return intent.hasExtra(name) ? intent.getStringExtra(name) : ""; + } @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "HoverUssdChannel"); eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "TransactionEvent"); + context = flutterPluginBinding.getApplicationContext(); eventChannel.setStreamHandler(this); channel.setMethodCallHandler(this); } - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { - HoverUssdApi hoverUssdApi = new HoverUssdApi(activity); - switch (call.method) { - case "HoverStartATransaction": - hoverUssdApi.sendUssd((String) call.argument("actionId"), - (HashMap) Objects.requireNonNull(call.argument("extras")), - (String) call.argument("theme")); - break; - case "Initialize": - - Hover.initialize(activity.getApplicationContext()); - if (call.argument("branding") != null && call.argument("logo") != null) { - Context context = activity.getApplicationContext(); - int id = context.getResources().getIdentifier((String) call.argument("logo"), "drawable", context.getPackageName()); - Hover.setBranding((String) call.argument("branding"), id, activity); - } - break; - default: - result.notImplemented(); - - } - - } - @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { channel.setMethodCallHandler(null); @@ -87,45 +67,144 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); - binding.addActivityResultListener(this); + + smsReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Map result = new HashMap<>(); + + result.put("state", "smsParsed"); + + result.put("action_id", intentNullAwareString(intent, "action_id")); + result.put("response_message", intentNullAwareString(intent, "response_message")); + result.put("status", intentNullAwareString(intent, "status")); + result.put("status_meaning", intentNullAwareString(intent, "status_meaning")); + result.put("status_description", intentNullAwareString(intent, "status_description")); + result.put("uuid", intentNullAwareString(intent, "uuid")); + result.put("im_hni", intentNullAwareString(intent, "im_hni")); + result.put("environment", intent.getIntExtra("environment", 0)); + result.put("request_timestamp", intent.getIntExtra("request_timestamp", 0)); + result.put("response_timestamp", intent.getIntExtra("response_timestamp", 0)); + + result.put("matched_parser_id", intentNullAwareString(intent, "matched_parser_id")); + result.put("messagetype", intentNullAwareString(intent, "messagetype")); + result.put("message_sender", intentNullAwareString(intent, "message_sender")); + result.put("regex", intentNullAwareString(intent, "regex")); + + + eventSink.success(result); + } + }; + + + activity.registerReceiver(smsReceiver + , new IntentFilter("com.lucdotdev.hover_ussd.CONFIRMED_TRANSACTION")); + } @Override public void onDetachedFromActivityForConfigChanges() { + activity.unregisterReceiver(smsReceiver); activity = null; } @Override public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); + activity.registerReceiver(smsReceiver + , new IntentFilter("com.lucdotdev.hover_ussd.CONFIRMED_TRANSACTION")); } @Override public void onDetachedFromActivity() { + activity.unregisterReceiver(smsReceiver); activity = null; } - @Override public boolean onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 0 && resultCode == Activity.RESULT_OK) { - Log.d("HOVER_MSG:", "The ussd action called succefully"); - eventSink.success("success"); + String uuid = data.hasExtra("uuid") ? data.getStringExtra("uuid") : ""; - return true; + Map result = new HashMap<>(); + result.put("state", "ussdSucceded"); + result.put("uuid", uuid); + if (data.hasExtra("session_messages")) { + String[] sessionMessages = data.getStringArrayExtra("session_messages"); + result.put("ussdSessionMessages", sessionMessages); + } - } else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED) { + eventSink.success(result); - Log.e("HOVER_ERROR:", "Hover ussd transaction error"); - eventSink.success("failed"); + return true; + + } + else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED) { + Map result = new HashMap<>(); + result.put("state", "ussdFailed"); + if (data != null) { + result.put("errorMessage", data.getStringExtra("error")); + } + eventSink.success(result); return true; } + return false; } + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { + HoverUssdApi hoverUssdApi = new HoverUssdApi(activity, context, eventSink); + switch (call.method) { + case "Initialize": + + Hover.initialize(activity); + if (call.argument("branding") != null && call.argument("logo") != null) { + String[] parts = ((String) call.argument("logo")).split("/"); + String resourceType = parts[0]; + String resourceName = parts[1]; + + int id = context.getResources().getIdentifier(resourceName, resourceType, context.getPackageName()); + Hover.setBranding((String) call.argument("branding"), id, context); + } + break; + case "HoverStartATransaction": + + hoverUssdApi.sendUssd( + (String) call.argument("actionId"), + call.hasArgument("extras") ? + (HashMap) Objects.requireNonNull(call.argument("extras")) + : new HashMap(), + call.hasArgument("theme") ? + (String) call.argument("theme") : + "", + call.hasArgument("header") ? + (String) call.argument("header") + : "", + call.hasArgument("initialProcessingMessage") ? + (String) call.argument("initialProcessingMessage") + : "", + call.hasArgument("finalMsgDisplayTime") ? + (int) call.argument("finalMsgDisplayTime") + : 5000, + false + + + ); + break; + + default: + result.notImplemented(); + + } + + } + + @Override public void onListen(Object arguments, EventChannel.EventSink events) { eventSink = events; @@ -135,6 +214,4 @@ public void onListen(Object arguments, EventChannel.EventSink events) { public void onCancel(Object arguments) { eventSink = null; } - - } \ No newline at end of file diff --git a/example/android/.settings/org.eclipse.buildship.core.prefs b/example/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..df7d455 --- /dev/null +++ b/example/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=C\:/Program Files/AdoptOpenJDK/jdk-11.0.10.9-hotspot +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/example/android/app/.classpath b/example/android/app/.classpath new file mode 100644 index 0000000..4a04201 --- /dev/null +++ b/example/android/app/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/example/android/app/.settings/org.eclipse.buildship.core.prefs b/example/android/app/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..b1886ad --- /dev/null +++ b/example/android/app/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +connection.project.dir=.. +eclipse.preferences.version=1 diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 3ace8bc..24cb535 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ In most cases you can leave this as-is, but you if you want to provide additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - + + - + \ No newline at end of file diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 1f83a33..bba3712 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -15,4 +15,36 @@ + + + + + + + + diff --git a/example/lib/main.dart b/example/lib/main.dart index 43392d2..b3ad6b4 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,16 +4,12 @@ import 'package:hover_ussd/hover_ussd.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); - HoverUssd.initialize(); + HoverUssd.initialize( + branding: 'Hover Ussd Example', logo: "mipmap/ic_launcher"); runApp(MyApp()); } -class MyApp extends StatefulWidget { - @override - _MyAppState createState() => _MyAppState(); -} - -class _MyAppState extends State { +class MyApp extends StatelessWidget { final HoverUssd _hoverUssd = HoverUssd(); @override @@ -21,34 +17,49 @@ class _MyAppState extends State { return MaterialApp( home: Scaffold( appBar: AppBar( - title: const Text('Plugin example app'), + title: const Text('hover ussd example'), ), - body: Center( - child: Row( - children: [ - FlatButton( - onPressed: () { - _hoverUssd.sendUssd( - actionId: "c6e45e62", extras: {"price": "4000"}); - }, - child: Text("Start Trasaction"), - ), - StreamBuilder( - stream: _hoverUssd.getUssdTransactionState, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.data == TransactionState.succesfull) { - return Text("succesfull"); - } else if (snapshot.data == - TransactionState.actionDowaloadFailed) { - return Text("action download failed"); - } else if (snapshot.data == TransactionState.failed) { - return Text("failed"); - } - return Text("no transaction"); - }, - ), - ], - ), + body: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TextButton( + onPressed: () { + _hoverUssd.sendUssd( + actionId: "c6e45e62", + extras: {"price": "4000"}, + theme: "myHoverTheme", + header: "transaction airtel", + showUserStepDescriptions: true); + }, + child: Text("Start Transaction"), + ), + StreamBuilder( + stream: _hoverUssd.getUssdTransactionState, + builder: (BuildContext context, snapshot) { + if (snapshot.data! is SmsParsed) { + return Text( + "Sms parsed : \n" + snapshot.data!.toMap().toString()); + } + + if (snapshot.data! is UssdSucceded) { + return Text( + "Ussd Succeded : \n" + snapshot.data!.toMap().toString()); + } + if (snapshot.data! is UssdLoading) { + return Text("loading..."); + } + if (snapshot.data! is UssdFailed) { + return Text( + "Ussd Failed : \n" + snapshot.data!.toMap().toString()); + } + if (snapshot.data! is EmptyState) { + return Text("Empty State"); + } + + return Text("No state"); + }, + ), + ], ), ), ); diff --git a/example/pubspec.lock b/example/pubspec.lock index 2e129c6..46b8712 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -73,7 +73,7 @@ packages: path: ".." relative: true source: path - version: "1.0.0" + version: "2.0.0" matcher: dependency: transitive description: diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart index 6eb59bc..fe59094 100644 --- a/lib/hover_ussd.dart +++ b/lib/hover_ussd.dart @@ -1,16 +1,196 @@ import 'dart:async'; + import 'package:flutter/services.dart'; -enum TransactionState { - // when the ussd code run succesfully +abstract class TransactionState { + TransactionState(); + + Map toMap(); +} + +/// when the message(sms) is successfully parsed +class SmsParsed extends TransactionState { + /// Unique Identifier for the transaction + final String? uuid; + + /// The action id from out supported operators page + final String? actionId; + + /// Full message used for parsing + final String? responseMessage; + + /// “pending”, “failed” or “succeeded” + final String? status; + + /// What you specified for the latest matched parser or one of the default failed cases above + final String? statusMeaning; + + /// Message you specified for the latest matched parser + final String? statusDescription; + + /// Unique identifier for the parser which matched, causing this transaction to update + final int? matchedParserId; + + /// “ussd” or “sms” + final String? messagetype; + + /// If SMS, the sender id from the parser form, null if USSD + final String? messageSender; - succesfull, - // when the ussd code failed; this can be caused by user - // dissmiss or request refuse - failed, - // this is when hover failed to dowload the list of your - // action , usualy when the app is not connected - actionDowaloadFailed + /// Regular expression you specified in the parser form + final String? regex; + + /// The Home Network Identifier (MCC + MNC) of the SIM used + final String? simHni; + + /// 0 for normal, 1 for debug, 2 for test + final int? environment; + + /// Time user initiated transaction (Unix time) + final int? requestTimestamp; + + /// Time at which the transaction last updated (SMS arrival or USSD finished) + final int? updateTimestamp; + + /// (depreciated) Same as updated_timestamp + final int? responseTimestamp; + + /// A HashMap object of all the extras you passed in using .extra(key, value) + final Map? inputExtras; + + /// A HashMap object of all named groups parsed out of the response message based on your regex + final Map? parsedVariables; + + /// Array of all USSD session messages in order encountered + final List? sessionMessages; + + SmsParsed( + {this.uuid, + this.actionId, + this.responseMessage, + this.status, + this.statusMeaning, + this.statusDescription, + this.matchedParserId, + this.messagetype, + this.messageSender, + this.regex, + this.simHni, + this.environment, + this.requestTimestamp, + this.updateTimestamp, + this.responseTimestamp, + this.inputExtras, + this.parsedVariables, + this.sessionMessages}); + @override + Map toMap() { + return { + 'uuid': uuid, + 'action_id': actionId, + 'response_message': responseMessage, + 'status': status, + 'status_meaning': statusMeaning, + 'status_description': statusDescription, + 'matched_parser_id': matchedParserId, + 'messagetype': messagetype, + 'message_sender': messageSender, + 'regex': regex, + 'sim_hni': simHni, + 'environment': environment, + 'request_timestamp': requestTimestamp, + 'update_timestamp': updateTimestamp, + 'response_timestamp': responseTimestamp, + 'input_extras': inputExtras, + 'parsed_variables': parsedVariables, + 'session_messages': sessionMessages, + }; + } + + factory SmsParsed.fromMap(Map json) { + return SmsParsed( + uuid: json['uuid'] as String?, + sessionMessages: json['session_messages'] as List?, + inputExtras: json['input_extras'] as Map?, + parsedVariables: json['parsed_variables'] as Map?, + responseTimestamp: json['response_timestamp'] as int?, + updateTimestamp: json['update_timestamp'] as int?, + requestTimestamp: json['request_timestamp'] as int?, + environment: json['environment'] as int?, + simHni: json['sim_hni'] as String?, + regex: json['regex'] as String?, + messageSender: json['message_sender'] as String?, + messagetype: json['messagetype'] as String?, + matchedParserId: json['matched_parser_id'] as int?, + statusDescription: json['status_description'] as String?, + statusMeaning: json['status_meaning'] as String?, + actionId: json['action_id'] as String?, + responseMessage: json['response_message'] as String?, + status: json['status'] as String?, + ); + } +} + +/// when ussd session run succesfully +class UssdSucceded extends TransactionState { + /// Unique Identifier for the transaction + final String? uuid; + + /// The action id from out supported operators page + final String? actionId; + + /// Full message used for parsing + final String? responseMessage; + + UssdSucceded({this.uuid, this.actionId, this.responseMessage}); + @override + Map toMap() { + return { + 'uuid': uuid, + 'action_id': actionId, + 'response_message': responseMessage, + }; + } + + factory UssdSucceded.fromMap(Map json) { + return UssdSucceded( + uuid: json['uuid'] as String?, + actionId: json['action_id'] as String?, + responseMessage: json['response_message'] as String?, + ); + } +} + +/// when the ussd code failed; this can be caused by user +/// dissmiss or request refuse +class UssdFailed extends TransactionState { + /// error message if ussd call failed + final String? errorMessage; + + UssdFailed({this.errorMessage}); + + factory UssdFailed.fromMap(Map json) { + return UssdFailed(errorMessage: json["errorMessage"]); + } + + @override + Map toMap() { + return {"errorMessage": errorMessage}; + } +} + +class UssdLoading extends TransactionState { + @override + Map toMap() { + return {}; + } +} + +class EmptyState extends TransactionState { + @override + Map toMap() { + throw UnimplementedError(); + } } class HoverUssd { @@ -18,49 +198,68 @@ class HoverUssd { static const EventChannel _eventChannel = EventChannel('TransactionEvent'); static Future initialize({String? branding, String? logo}) async { - await _methodChannel.invokeMethod("Initialize", - {"branding": branding ?? "Flutter App", "logo": logo ?? "ic_launcher"}); + await _methodChannel.invokeMethod("Initialize", { + "branding": branding ?? "Flutter App", + "logo": logo ?? "mipmap/ic_launcher" + }); } Stream? _onTransactionStateChanged; - // when you call send ussd, automatically the ussd procces begin - // and the permission is autaumatically handle - // this method came with 3 params - // @actionId is the id for the action you trying to call - // cc: https://docs.usehover.com/ussd - // @extras is the extras variable which came with action id , you can - // convigure tha on : https://www.usehover.com/ - // optional: if you want to customize the ussd theme then you can add a theme at - // your style.xml and then add the style string on @theme param + /// when you call send ussd, automatically the ussd procces begin + /// and the permission is autaumatically handled + /// this method came with 7 params + /// [actionId] is the id for the action you trying to call + /// see + /// [extras] is the extras variable which came with action id , you can + /// optional: if you want to customize the ussd theme then you can add a theme at + /// your style.xml and then add the style string on [theme] param + /// see for other params Future sendUssd( {required String actionId, Map? extras, - String? theme}) async => - await _methodChannel.invokeMethod("HoverStartATransaction", - {"actionId": actionId, "extras": extras ?? {}, "theme": theme}); - - // this is for getting response of the current ussd session + String? theme, + String? header, + String? initialProcessingMessage, + int? finalMsgDisplayTime, + bool? showUserStepDescriptions}) async => + await _methodChannel.invokeMethod("HoverStartATransaction", { + "actionId": actionId, + "extras": extras ?? {}, + "theme": theme, + "header": header, + "initialProcessingMessage": initialProcessingMessage, + "finalMsgDisplayTime": finalMsgDisplayTime ?? 5000 + }); + /// this is for getting response of the current ussd session Stream? get getUssdTransactionState { if (_onTransactionStateChanged == null) { - _onTransactionStateChanged = _eventChannel - .receiveBroadcastStream() - .map((dynamic event) => _parseTransactionState(event)); + _onTransactionStateChanged = + _eventChannel.receiveBroadcastStream().map((dynamic event) { + return _parseTransactionState(event)!; + }); } return _onTransactionStateChanged; } - TransactionState _parseTransactionState(String? state) { - switch (state) { - case "success": - return TransactionState.succesfull; - case "failed": - return TransactionState.failed; - case "actionDownloadFailed": - return TransactionState.actionDowaloadFailed; + TransactionState? _parseTransactionState(dynamic result) { + switch (result["state"]) { + case "smsParsed": + print("smsParsed"); + return SmsParsed.fromMap(result); + case "ussdSucceded": + print("ussdSucceded"); + return UssdSucceded.fromMap(result); + case "ussdFailed": + print("ussdFailed"); + return UssdFailed.fromMap(result); + case "ussdLoading": + print("ussdLoading"); + return UssdLoading(); + default: - throw ArgumentError('$state'); + return EmptyState(); } } } diff --git a/pubspec.lock b/pubspec.lock index 1ab9fcf..4935c14 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -68,7 +68,7 @@ packages: source: hosted version: "0.12.10" meta: - dependency: "direct main" + dependency: transitive description: name: meta url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index b2ea127..247bc52 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,8 @@ name: hover_ussd description: A flutter plugin to make payments by usehover.com ussd gateway using Android Intent and receiving the transaction information back in response. -version: 1.0.0 +version: 2.0.0 + + homepage: https://github.com/lucdotdev/hover_ussd @@ -12,7 +14,7 @@ environment: dependencies: flutter: sdk: flutter - meta: ^1.1.8 + dev_dependencies: flutter_test: diff --git a/test/hover_ussd_test.dart b/test/hover_ussd_test.dart index 99b96ec..e29aefe 100644 --- a/test/hover_ussd_test.dart +++ b/test/hover_ussd_test.dart @@ -1,6 +1,5 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:hover_ussd/hover_ussd.dart'; void main() { const MethodChannel channel = MethodChannel('hover_ussd'); From 78c99e33708196783f009f6b259899d2faa0234e Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Tue, 16 Nov 2021 13:03:22 +0200 Subject: [PATCH 18/36] fix receive null result --- README.md | 12 ++++++++---- example/lib/main.dart | 14 +++++++------- lib/hover_ussd.dart | 11 +++-------- test/hover_ussd_test.dart | 5 +++++ 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 1562ef4..9193615 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ A flutter plugin implemanting usehover.com ussd gateway sdk using Android Intent ```dart void main() { WidgetsFlutterBinding.ensureInitialized(); - HoverUssd.initialize(branding: 'hover ussd example'); + HoverUssd.initialize(branding: 'Hover Ussd Example'); runApp(MyApp()); } @@ -39,7 +39,7 @@ class MyApp extends StatelessWidget { return MaterialApp( home: Scaffold( appBar: AppBar( - title: const Text('hover ussd example'), + title: const Text('Hover Ussd Example'), ), body: Center( child: Row( @@ -50,7 +50,7 @@ class MyApp extends StatelessWidget { actionId: "c6e45e62", extras: {"price": "4000"}, theme: "myHoverTheme", - header: "transaction airtel", + header: "Hover Ussd Example", showUserStepDescriptions: true); }, child: Text("Start Trasaction"), @@ -88,7 +88,11 @@ class MyApp extends StatelessWidget { ``` ## Customization - +To use your logo on the processing screen +```dart + HoverUssd.initialize( + branding: 'Hover Ussd Example', logo: "mipmap/ic_launcher"); +``` ## Important * **This is a unofficial plugin** ## Credit diff --git a/example/lib/main.dart b/example/lib/main.dart index b3ad6b4..264c3cd 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -17,7 +17,7 @@ class MyApp extends StatelessWidget { return MaterialApp( home: Scaffold( appBar: AppBar( - title: const Text('hover ussd example'), + title: const Text('Hover Ussd Example'), ), body: Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -28,7 +28,7 @@ class MyApp extends StatelessWidget { actionId: "c6e45e62", extras: {"price": "4000"}, theme: "myHoverTheme", - header: "transaction airtel", + header: "Hover Ussd Example", showUserStepDescriptions: true); }, child: Text("Start Transaction"), @@ -36,23 +36,23 @@ class MyApp extends StatelessWidget { StreamBuilder( stream: _hoverUssd.getUssdTransactionState, builder: (BuildContext context, snapshot) { - if (snapshot.data! is SmsParsed) { + if (snapshot.data is SmsParsed) { return Text( "Sms parsed : \n" + snapshot.data!.toMap().toString()); } - if (snapshot.data! is UssdSucceded) { + if (snapshot.data is UssdSucceded) { return Text( "Ussd Succeded : \n" + snapshot.data!.toMap().toString()); } - if (snapshot.data! is UssdLoading) { + if (snapshot.data is UssdLoading) { return Text("loading..."); } - if (snapshot.data! is UssdFailed) { + if (snapshot.data is UssdFailed) { return Text( "Ussd Failed : \n" + snapshot.data!.toMap().toString()); } - if (snapshot.data! is EmptyState) { + if (snapshot.data is EmptyState) { return Text("Empty State"); } diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart index fe59094..482b5f5 100644 --- a/lib/hover_ussd.dart +++ b/lib/hover_ussd.dart @@ -237,27 +237,22 @@ class HoverUssd { if (_onTransactionStateChanged == null) { _onTransactionStateChanged = _eventChannel.receiveBroadcastStream().map((dynamic event) { - return _parseTransactionState(event)!; + return _parseTransactionState(event['state'] as String, event); }); } return _onTransactionStateChanged; } - TransactionState? _parseTransactionState(dynamic result) { - switch (result["state"]) { + TransactionState _parseTransactionState(String state, dynamic result) { + switch (state) { case "smsParsed": - print("smsParsed"); return SmsParsed.fromMap(result); case "ussdSucceded": - print("ussdSucceded"); return UssdSucceded.fromMap(result); case "ussdFailed": - print("ussdFailed"); return UssdFailed.fromMap(result); case "ussdLoading": - print("ussdLoading"); return UssdLoading(); - default: return EmptyState(); } diff --git a/test/hover_ussd_test.dart b/test/hover_ussd_test.dart index e29aefe..c916de9 100644 --- a/test/hover_ussd_test.dart +++ b/test/hover_ussd_test.dart @@ -1,5 +1,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:hover_ussd/hover_ussd.dart'; void main() { const MethodChannel channel = MethodChannel('hover_ussd'); @@ -12,6 +13,10 @@ void main() { }); }); + group("Test Plugin", () { + test("Initialization", () {}); + }); + tearDown(() { channel.setMockMethodCallHandler(null); }); From 17567c31133d2f00d6dc96ba264d459b7212959a Mon Sep 17 00:00:00 2001 From: lucdotdev <55983266+lucdotdev@users.noreply.github.com> Date: Tue, 16 Nov 2021 13:12:38 +0200 Subject: [PATCH 19/36] update workflow --- .github/workflows/main.yml | 60 +++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0360f7c..1df0c5b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,26 +1,46 @@ name: build + +# This workflow is triggered on pushes to the repository. + on: - pull_request: - paths: - - "**" push: - branches: - - master - + +# on: push # Default will running for every branch. + jobs: - android: - runs-on: windows-latest - timeout-minutes: 30 + build: + # This job will run on ubuntu virtual machine + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: subosito/flutter-action@v1.3.0 - with: - flutter-version: '1.21.0-9.0.pre' - channel: 'dev' - - name: Install Dependencies - run: flutter packages get - - name: Build example - run: | - cd example - flutter build apk + + # Setup Java environment in order to build the Android app. + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '12.x' + + # Setup the flutter environment. + - uses: subosito/flutter-action@v1 + with: + channel: 'stable' # 'dev', 'alpha', default to: 'stable' + # flutter-version: '1.12.x' # you can also specify exact version of flutter + + # Get flutter dependencies. + - run: flutter pub get + + # Check for any formatting issues in the code. + - run: flutter format --set-exit-if-changed . + + # Statically analyze the Dart code for any errors. + - run: flutter analyze . + + # Build apk. + - run: flutter build apk + + # Upload generated apk to the artifacts. + - uses: actions/upload-artifact@v1 + with: + name: release-apk + path: build/app/outputs/apk/release/app-release.apk + From 88e68218a4f4360f353a4a8755c524a008041afe Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Tue, 16 Nov 2021 13:35:31 +0200 Subject: [PATCH 20/36] change sendUssd to startTransaction --- example/lib/main.dart | 2 +- lib/hover_ussd.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 264c3cd..c584e49 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -24,7 +24,7 @@ class MyApp extends StatelessWidget { children: [ TextButton( onPressed: () { - _hoverUssd.sendUssd( + _hoverUssd.startTransaction( actionId: "c6e45e62", extras: {"price": "4000"}, theme: "myHoverTheme", diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart index 482b5f5..d6a202d 100644 --- a/lib/hover_ussd.dart +++ b/lib/hover_ussd.dart @@ -215,7 +215,7 @@ class HoverUssd { /// optional: if you want to customize the ussd theme then you can add a theme at /// your style.xml and then add the style string on [theme] param /// see for other params - Future sendUssd( + Future startTransaction( {required String actionId, Map? extras, String? theme, From bb3820066ea0a9718a7ea2e0dc870e57c81e2992 Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Tue, 16 Nov 2021 13:35:44 +0200 Subject: [PATCH 21/36] update readme --- CHANGELOG.md | 1 + README.md | 162 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 122 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d627291..d5364c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## 2.0.0 * revert to hover standart +* change sendUssd() to startTransaction() ## 1.0.0a * change from hover standard to hover noSms * You can now customize the hover the by passing branding or drawable to the HoverUssd Contruction diff --git a/README.md b/README.md index 9193615..0d5698c 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ A flutter plugin implemanting usehover.com ussd gateway sdk using Android Intent ```dart void main() { WidgetsFlutterBinding.ensureInitialized(); - HoverUssd.initialize(branding: 'Hover Ussd Example'); + HoverUssd.initialize( + branding: 'Hover Ussd Example', logo: "mipmap/ic_launcher"); runApp(MyApp()); } @@ -41,58 +42,137 @@ class MyApp extends StatelessWidget { appBar: AppBar( title: const Text('Hover Ussd Example'), ), - body: Center( - child: Row( - children: [ - FlatButton( - onPressed: () { - _hoverUssd.sendUssd( - actionId: "c6e45e62", - extras: {"price": "4000"}, - theme: "myHoverTheme", - header: "Hover Ussd Example", - showUserStepDescriptions: true); - }, - child: Text("Start Trasaction"), - ), - StreamBuilder( - stream: _hoverUssd.getUssdTransactionState, - builder: (BuildContext context, snapshot) { - if (snapshot.data is SmsParsed) { - return Text( - "Sms parsed : \n" + snapshot.data!.toMap().toString()); - } - - if (snapshot.data is UssdSucceded) { - return Text("Ussd Succeded : \n" + - snapshot.data!.toMap().toString()); - } - if (snapshot.data is UssdLoading) { - return Text("loading..."); - } - if (snapshot.data is UssdFailed) { - return Text( - "Ussd Failed : \n" + snapshot.data!.toMap().toString()); - } - - return Text(""); - }, - ), - ], - ), + body: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TextButton( + onPressed: () { + _hoverUssd.startTransaction( + actionId: "c6e45e62", + extras: {"price": "4000"}, + theme: "myHoverTheme", + header: "Hover Ussd Example", + showUserStepDescriptions: true); + }, + child: Text("Start Transaction"), + ), + StreamBuilder( + stream: _hoverUssd.getUssdTransactionState, + builder: (BuildContext context, snapshot) { + if (snapshot.data is SmsParsed) { + return Text( + "Sms parsed : \n" + snapshot.data!.toMap().toString()); + } + + if (snapshot.data is UssdSucceded) { + return Text( + "Ussd Succeded : \n" + snapshot.data!.toMap().toString()); + } + if (snapshot.data is UssdLoading) { + return Text("loading..."); + } + if (snapshot.data is UssdFailed) { + return Text( + "Ussd Failed : \n" + snapshot.data!.toMap().toString()); + } + if (snapshot.data is EmptyState) { + return Text("Empty State"); + } + + return Text("No state"); + }, + ), + ], ), ), ); } } + + ``` ## Customization -To use your logo on the processing screen + +* ### To use your logo on the processing screen ```dart HoverUssd.initialize( branding: 'Hover Ussd Example', logo: "mipmap/ic_launcher"); ``` + + +* ### To use your own theme style + +#### add your style to android/app/main/values/styles.xml +```xml + + + + + + + + + + + + + + +``` + +#### add the theme name when calling **HoverUssd().startTransaction()** +```dart + _hoverUssd.startTransaction( + theme: "myHoverTheme",); +``` +* ### Others customizations +```dart +startTransaction( + {required String actionId, + Map? extras, + String? theme, + String? header, + String? initialProcessingMessage, + int? finalMsgDisplayTime, + bool? showUserStepDescriptions}) +``` + ## Important * **This is a unofficial plugin** ## Credit From be56fde08335ab5a1e043fbf3904782de1db27a6 Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Tue, 16 Nov 2021 13:51:28 +0200 Subject: [PATCH 22/36] fix workflw --- .github/workflows/main.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1df0c5b..93a232e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,13 +27,7 @@ jobs: # Get flutter dependencies. - run: flutter pub get - - # Check for any formatting issues in the code. - - run: flutter format --set-exit-if-changed . - - # Statically analyze the Dart code for any errors. - - run: flutter analyze . - + # Build apk. - run: flutter build apk From e8303fd8deabdce3587a0651ca275c98109682f6 Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Tue, 23 Nov 2021 14:24:04 +0200 Subject: [PATCH 23/36] fix #5 --- .../lucdotdev/hover_ussd/HoverUssdApi.java | 6 +- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 133 +++++++++--------- example/lib/main.dart | 131 ++++++++++------- lib/hover_ussd.dart | 86 ++++++++++- 4 files changed, 239 insertions(+), 117 deletions(-) diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index 216324c..cdfb8a7 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -12,7 +12,6 @@ import java.util.HashMap; import java.util.Map; -import io.flutter.plugin.common.EventChannel; public class HoverUssdApi { @@ -22,11 +21,10 @@ public class HoverUssdApi { private Context context; - private EventChannel.EventSink eventSink; - public HoverUssdApi(Activity activity, Context context,EventChannel.EventSink eventSink) { + public HoverUssdApi(Activity activity, Context context) { this.activity = activity; - this.eventSink = eventSink; + this.context = context; } diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index d268237..72e75cd 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -15,10 +15,12 @@ import androidx.annotation.RequiresApi; import com.hover.sdk.api.Hover; +import com.hover.sdk.transactions.Transaction; import java.io.FileDescriptor; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -37,38 +39,97 @@ public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener, EventChannel.StreamHandler { + private MethodChannel channel; - Activity activity; - Context context; + private Activity activity; + + private EventChannel eventChannel; private EventChannel.EventSink eventSink; private BroadcastReceiver smsReceiver; - private String intentNullAwareString(Intent intent, String name) { - return intent.hasExtra(name) ? intent.getStringExtra(name) : ""; - } @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "HoverUssdChannel"); eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "TransactionEvent"); - context = flutterPluginBinding.getApplicationContext(); + eventChannel.setStreamHandler(this); channel.setMethodCallHandler(this); } + private String intentNullAwareString(Intent intent, String name) { + return intent.hasExtra(name) ? intent.getStringExtra(name) : ""; + } + + + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { + HoverUssdApi hoverUssdApi = new HoverUssdApi(activity, activity.getApplicationContext()); + switch (call.method) { + case "Initialize": + + Hover.initialize(activity); + if (call.argument("branding") != null && call.argument("logo") != null) { + String[] parts = ((String) call.argument("logo")).split("/"); + String resourceType = parts[0]; + String resourceName = parts[1]; + + int id =activity.getApplicationContext().getResources().getIdentifier(resourceName, resourceType, activity.getApplicationContext().getPackageName()); + Hover.setBranding((String) call.argument("branding"), id, activity.getApplicationContext()); + } + break; + case "HoverStartATransaction": + Map resultJson = new HashMap<>(); + resultJson.put("state", "ussdLoading"); + eventSink.success(resultJson); + hoverUssdApi.sendUssd( + (String) call.argument("actionId"), + call.hasArgument("extras") ? + (HashMap) Objects.requireNonNull(call.argument("extras")) + : new HashMap(), + call.hasArgument("theme") ? + (String) call.argument("theme") : + "", + call.hasArgument("header") ? + (String) call.argument("header") + : "", + call.hasArgument("initialProcessingMessage") ? + (String) call.argument("initialProcessingMessage") + : "", + call.hasArgument("finalMsgDisplayTime") ? + (int) call.argument("finalMsgDisplayTime") + : 5000, + false + + + ); + break; + default: + result.notImplemented(); + + } + + } + @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { channel.setMethodCallHandler(null); eventChannel.setStreamHandler(null); + } @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); + binding.addActivityResultListener(this); - + + smsReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -101,28 +162,24 @@ public void onReceive(Context context, Intent intent) { activity.registerReceiver(smsReceiver , new IntentFilter("com.lucdotdev.hover_ussd.CONFIRMED_TRANSACTION")); - } @Override public void onDetachedFromActivityForConfigChanges() { - activity.unregisterReceiver(smsReceiver); activity = null; } @Override public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); - activity.registerReceiver(smsReceiver - , new IntentFilter("com.lucdotdev.hover_ussd.CONFIRMED_TRANSACTION")); } @Override public void onDetachedFromActivity() { - activity.unregisterReceiver(smsReceiver); activity = null; } + @Override public boolean onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 0 && resultCode == Activity.RESULT_OK) { @@ -151,60 +208,9 @@ else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED) { return true; } - return false; } - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - HoverUssdApi hoverUssdApi = new HoverUssdApi(activity, context, eventSink); - switch (call.method) { - case "Initialize": - - Hover.initialize(activity); - if (call.argument("branding") != null && call.argument("logo") != null) { - String[] parts = ((String) call.argument("logo")).split("/"); - String resourceType = parts[0]; - String resourceName = parts[1]; - - int id = context.getResources().getIdentifier(resourceName, resourceType, context.getPackageName()); - Hover.setBranding((String) call.argument("branding"), id, context); - } - break; - case "HoverStartATransaction": - - hoverUssdApi.sendUssd( - (String) call.argument("actionId"), - call.hasArgument("extras") ? - (HashMap) Objects.requireNonNull(call.argument("extras")) - : new HashMap(), - call.hasArgument("theme") ? - (String) call.argument("theme") : - "", - call.hasArgument("header") ? - (String) call.argument("header") - : "", - call.hasArgument("initialProcessingMessage") ? - (String) call.argument("initialProcessingMessage") - : "", - call.hasArgument("finalMsgDisplayTime") ? - (int) call.argument("finalMsgDisplayTime") - : 5000, - false - - - ); - break; - - default: - result.notImplemented(); - - } - - } - - @Override public void onListen(Object arguments, EventChannel.EventSink events) { eventSink = events; @@ -214,4 +220,5 @@ public void onListen(Object arguments, EventChannel.EventSink events) { public void onCancel(Object arguments) { eventSink = null; } + } \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index c584e49..9cb22e8 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:hover_ussd/hover_ussd.dart'; @@ -6,61 +8,96 @@ void main() { WidgetsFlutterBinding.ensureInitialized(); HoverUssd.initialize( branding: 'Hover Ussd Example', logo: "mipmap/ic_launcher"); - runApp(MyApp()); + runApp(App()); } -class MyApp extends StatelessWidget { - final HoverUssd _hoverUssd = HoverUssd(); +class App extends StatelessWidget { + const App({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Hover Ussd Example'), - ), - body: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - TextButton( - onPressed: () { - _hoverUssd.startTransaction( - actionId: "c6e45e62", - extras: {"price": "4000"}, - theme: "myHoverTheme", - header: "Hover Ussd Example", - showUserStepDescriptions: true); - }, - child: Text("Start Transaction"), - ), - StreamBuilder( - stream: _hoverUssd.getUssdTransactionState, - builder: (BuildContext context, snapshot) { - if (snapshot.data is SmsParsed) { - return Text( - "Sms parsed : \n" + snapshot.data!.toMap().toString()); - } + home: MyApp(), + ); + } +} + +class MyApp extends StatefulWidget { + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + final HoverUssd _hoverUssd = HoverUssd(); + + late final StreamSubscription transactionListening; + @override + void initState() { + transactionListening = _hoverUssd.getUssdTransactionState!.listen((event) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(event.toMap().toString()))); + }); + super.initState(); + } + + @override + void dispose() { + transactionListening.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Hover Ussd Example'), + ), + body: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: () { + _hoverUssd.startTransaction( + actionId: "c6e45e62", + extras: {"price": "4000"}, + theme: "myHoverTheme", + header: "Hover Ussd Example", + showUserStepDescriptions: true); + }, + child: Text("Start Transaction"), + ), + StreamBuilder( + stream: _hoverUssd.getUssdTransactionState, + builder: (BuildContext context, snapshot) { + if (snapshot.data is SmsParsed) { + return Text( + "Sms parsed : \n" + snapshot.data!.toMap().toString()); + } - if (snapshot.data is UssdSucceded) { - return Text( - "Ussd Succeded : \n" + snapshot.data!.toMap().toString()); - } - if (snapshot.data is UssdLoading) { - return Text("loading..."); - } - if (snapshot.data is UssdFailed) { - return Text( - "Ussd Failed : \n" + snapshot.data!.toMap().toString()); - } - if (snapshot.data is EmptyState) { - return Text("Empty State"); - } + if (snapshot.data is UssdSucceded) { + return Text("Ussd Succeded : \n" + + snapshot.data!.toMap().toString()); + } + if (snapshot.data is UssdLoading) { + return Text("loading..."); + } + if (snapshot.data is UssdFailed) { + return Text( + "Ussd Failed : \n" + snapshot.data!.toMap().toString()); + } + if (snapshot.data is EmptyState) { + return Text("Empty State"); + } - return Text("No state"); - }, - ), - ], - ), + return Text("No state"); + }, + ), + ], + ), + ], ), ); } diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart index d6a202d..c204179 100644 --- a/lib/hover_ussd.dart +++ b/lib/hover_ussd.dart @@ -2,6 +2,67 @@ import 'dart:async'; import 'package:flutter/services.dart'; +class Transaction { + final String? id; + final String? actionId; + final String? uuid; + final String? status; + final String? category; + final String? userMessage; + final String? networkHni; + final String? inputExtras; + final String? parsedVariables; + final List? ussdMessages; + final List? enteredValues; + final List? smsHits; + final List? smsMisses; + final List? logMessages; + final List? matchedParsers; + final int? reqTimestamp; + final int? updatedTimestamp; + + Transaction( + {this.uuid, + this.id, + this.actionId, + this.status, + this.category, + this.userMessage, + this.networkHni, + this.inputExtras, + this.parsedVariables, + this.ussdMessages, + this.enteredValues, + this.smsHits, + this.smsMisses, + this.logMessages, + this.matchedParsers, + this.reqTimestamp, + this.updatedTimestamp}); + + factory Transaction.fromMap(Map map) { + return Transaction( + uuid: map['uuid'], + id: map['id'], + actionId: map['actionId'], + status: map['status'], + category: map['category'], + userMessage: map['userMessage'], + networkHni: map['networkHni'], + inputExtras: map['inputExtras'], + parsedVariables: map['parsedVariables'], + ussdMessages: map['ussdMessages'], + enteredValues: map['enteredValues'], + smsHits: map['smsHits'], + smsMisses: map['smsMisses'], + logMessages: map['logMessages'], + matchedParsers: map['matchedParsers'], + reqTimestamp: map['reqTimestamp'], + updatedTimestamp: map['updatedTimestamp'], + ); + } +} + abstract class TransactionState { TransactionState(); @@ -182,7 +243,7 @@ class UssdFailed extends TransactionState { class UssdLoading extends TransactionState { @override Map toMap() { - return {}; + throw UnimplementedError(); } } @@ -232,18 +293,37 @@ class HoverUssd { "finalMsgDisplayTime": finalMsgDisplayTime ?? 5000 }); + Future> getAllTransactions() async { + List> result = + await _methodChannel.invokeMethod("HoverStartATransaction"); + List transctions = []; + for (final item in result) { + transctions.add(Transaction()); + } + return transctions; + } + /// this is for getting response of the current ussd session Stream? get getUssdTransactionState { if (_onTransactionStateChanged == null) { _onTransactionStateChanged = _eventChannel.receiveBroadcastStream().map((dynamic event) { - return _parseTransactionState(event['state'] as String, event); + try { + print(event); + TransactionState transactionState = _parseTransactionState( + event['state'], Map.from(event)); + return transactionState; + } catch (e) { + print(e); + return EmptyState(); + } }); } return _onTransactionStateChanged; } - TransactionState _parseTransactionState(String state, dynamic result) { + TransactionState _parseTransactionState( + String state, Map result) { switch (state) { case "smsParsed": return SmsParsed.fromMap(result); From 4429b19564f529f2653ec4b1b029a37ed18d919a Mon Sep 17 00:00:00 2001 From: lucdotdev <55983266+lucdotdev@users.noreply.github.com> Date: Wed, 24 Nov 2021 00:19:47 +0200 Subject: [PATCH 24/36] fix workflow --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 93a232e..4b81514 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,7 +24,7 @@ jobs: with: channel: 'stable' # 'dev', 'alpha', default to: 'stable' # flutter-version: '1.12.x' # you can also specify exact version of flutter - + - run : cd example # Get flutter dependencies. - run: flutter pub get From 058c1487c2a48c4418630fbe8a153d8b45187331 Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Thu, 7 Mar 2024 21:13:12 +0200 Subject: [PATCH 25/36] canditate 1 --- ..._02_03_2024_06_35__Default_Changelist_.xml | 4 + android/.classpath | 6 - android/.gitignore | 1 + android/.idea/codeStyles/Project.xml | 116 ------ android/.idea/gradle.xml | 16 - android/.idea/modules.xml | 8 - android/.idea/vcs.xml | 6 - android/.project | 34 -- .../org.eclipse.buildship.core.prefs | 13 - android/build.gradle | 47 ++- android/gradle.properties | 4 - android/gradle/wrapper/gradle-wrapper.jar | Bin 53636 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - android/gradlew | 160 --------- android/gradlew.bat | 90 ----- android/settings.gradle | 2 + android/src/main/AndroidManifest.xml | 5 +- .../lucdotdev/hover_ussd/HoverUssdApi.java | 74 +--- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 250 ++----------- .../hover_ussd/HoverUssdPluginTest.java | 29 ++ example/.gitignore | 41 --- example/.metadata | 10 - example/README.md | 16 - example/android/.gitignore | 11 - example/android/.project | 28 -- .../org.eclipse.buildship.core.prefs | 13 - example/android/app/.classpath | 6 - example/android/app/.project | 34 -- .../org.eclipse.buildship.core.prefs | 2 - example/android/app/build.gradle | 54 --- .../android/app/src/debug/AndroidManifest.xml | 7 - .../android/app/src/main/AndroidManifest.xml | 52 --- .../hover_ussd_example/MainActivity.java | 6 - .../main/res/drawable/launch_background.xml | 12 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 0 bytes .../app/src/main/res/values/styles.xml | 50 --- .../app/src/profile/AndroidManifest.xml | 7 - example/android/build.gradle | 29 -- example/android/gradle.properties | 4 - .../gradle/wrapper/gradle-wrapper.properties | 6 - example/android/settings.gradle | 11 - example/ios/Flutter/Generated.xcconfig | 13 - .../ios/Flutter/flutter_export_environment.sh | 13 - .../ios/Runner/GeneratedPluginRegistrant.h | 19 - .../ios/Runner/GeneratedPluginRegistrant.m | 14 - example/lib/main.dart | 104 ------ example/pubspec.lock | 161 --------- example/pubspec.yaml | 72 ---- example/test/widget_test.dart | 12 - lib/hover_ussd.dart | 340 ------------------ test/hover_ussd_test.dart | 32 +- 55 files changed, 118 insertions(+), 1931 deletions(-) create mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35__Default_Changelist_.xml delete mode 100644 android/.classpath delete mode 100644 android/.idea/codeStyles/Project.xml delete mode 100644 android/.idea/gradle.xml delete mode 100644 android/.idea/modules.xml delete mode 100644 android/.idea/vcs.xml delete mode 100644 android/.project delete mode 100644 android/.settings/org.eclipse.buildship.core.prefs delete mode 100644 android/gradle.properties delete mode 100644 android/gradle/wrapper/gradle-wrapper.jar delete mode 100644 android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 android/gradlew delete mode 100644 android/gradlew.bat create mode 100644 android/src/test/java/com/lucdotdev/hover_ussd/HoverUssdPluginTest.java delete mode 100644 example/.gitignore delete mode 100644 example/.metadata delete mode 100644 example/README.md delete mode 100644 example/android/.gitignore delete mode 100644 example/android/.project delete mode 100644 example/android/.settings/org.eclipse.buildship.core.prefs delete mode 100644 example/android/app/.classpath delete mode 100644 example/android/app/.project delete mode 100644 example/android/app/.settings/org.eclipse.buildship.core.prefs delete mode 100644 example/android/app/build.gradle delete mode 100644 example/android/app/src/debug/AndroidManifest.xml delete mode 100644 example/android/app/src/main/AndroidManifest.xml delete mode 100644 example/android/app/src/main/java/com/lucdotdev/hover_ussd_example/MainActivity.java delete mode 100644 example/android/app/src/main/res/drawable/launch_background.xml delete mode 100644 example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 example/android/app/src/main/res/values/styles.xml delete mode 100644 example/android/app/src/profile/AndroidManifest.xml delete mode 100644 example/android/build.gradle delete mode 100644 example/android/gradle.properties delete mode 100644 example/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 example/android/settings.gradle delete mode 100644 example/ios/Flutter/Generated.xcconfig delete mode 100644 example/ios/Flutter/flutter_export_environment.sh delete mode 100644 example/ios/Runner/GeneratedPluginRegistrant.h delete mode 100644 example/ios/Runner/GeneratedPluginRegistrant.m delete mode 100644 example/lib/main.dart delete mode 100644 example/pubspec.lock delete mode 100644 example/pubspec.yaml delete mode 100644 example/test/widget_test.dart delete mode 100644 lib/hover_ussd.dart diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35__Default_Changelist_.xml b/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35__Default_Changelist_.xml new file mode 100644 index 0000000..8addb6d --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35__Default_Changelist_.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/android/.classpath b/android/.classpath deleted file mode 100644 index 4a04201..0000000 --- a/android/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/android/.gitignore b/android/.gitignore index c6cbe56..161bdcd 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -6,3 +6,4 @@ .DS_Store /build /captures +.cxx diff --git a/android/.idea/codeStyles/Project.xml b/android/.idea/codeStyles/Project.xml deleted file mode 100644 index 681f41a..0000000 --- a/android/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - -
- - - - xmlns:android - - ^$ - - - -
-
- - - - xmlns:.* - - ^$ - - - BY_NAME - -
-
- - - - .*:id - - http://schemas.android.com/apk/res/android - - - -
-
- - - - .*:name - - http://schemas.android.com/apk/res/android - - - -
-
- - - - name - - ^$ - - - -
-
- - - - style - - ^$ - - - -
-
- - - - .* - - ^$ - - - BY_NAME - -
-
- - - - .* - - http://schemas.android.com/apk/res/android - - - ANDROID_ATTRIBUTE_ORDER - -
-
- - - - .* - - .* - - - BY_NAME - -
-
-
-
-
-
\ No newline at end of file diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml deleted file mode 100644 index 178312e..0000000 --- a/android/.idea/gradle.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/android/.idea/modules.xml b/android/.idea/modules.xml deleted file mode 100644 index 9dddca5..0000000 --- a/android/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/android/.idea/vcs.xml b/android/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/android/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android/.project b/android/.project deleted file mode 100644 index 32b873b..0000000 --- a/android/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - hover_ussd - Project android created by Buildship. - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.buildship.core.gradleprojectnature - - - - 1630668570936 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index 2378e3a..0000000 --- a/android/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -arguments= -auto.sync=false -build.scans.enabled=false -connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) -connection.project.dir=../example/android -eclipse.preferences.version=1 -gradle.user.home= -java.home=C\:/Program Files/AdoptOpenJDK/jdk-11.0.10.9-hotspot -jvm.arguments= -offline.mode=false -override.workspace.settings=true -show.console.view=true -show.executions.view=true diff --git a/android/build.gradle b/android/build.gradle index f986d89..12e4bf3 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,47 +1,56 @@ +package android + group 'com.lucdotdev.hover_ussd' version '1.0' buildscript { repositories { google() - jcenter() - maven { url "http://maven.usehover.com/releases" } + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.android.tools.build:gradle:7.3.0' } - } - - rootProject.allprojects { - gradle.projectsEvaluated { - tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" - } - } repositories { google() - jcenter() - maven { url "http://maven.usehover.com/releases" } + mavenCentral() } } apply plugin: 'com.android.library' android { - compileSdkVersion 29 + if (project.android.hasProperty("namespace")) { + namespace 'com.lucdotdev.hover_ussd' + } - defaultConfig { - minSdkVersion 18 + compileSdkVersion 33 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } - lintOptions { - disable 'InvalidPackage' + + defaultConfig { + minSdkVersion 19 } + dependencies { + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.mockito:mockito-core:5.0.0' + } - implementation 'com.hover:android-sdk:1.7.2' + testOptions { + unitTests.all { + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } } } diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index 38c8d45..0000000 --- a/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true -android.useAndroidX=true -android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 13372aef5e24af05341d49695ee84e5f9b594659..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53636 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ⅅ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 H-+%o-(+5aQ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 01a286e..0000000 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/android/gradlew b/android/gradlew deleted file mode 100644 index 9d82f78..0000000 --- a/android/gradlew +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat deleted file mode 100644 index 8a0b282..0000000 --- a/android/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/android/settings.gradle b/android/settings.gradle index b4034f0..b70c813 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1 +1,3 @@ +package android + rootProject.name = 'hover_ussd' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 9b9c0d5..ed8aca5 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,6 +1,3 @@ - - + package="com.lucdotdev.hover_ussd"> diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index cdfb8a7..1ff8dec 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -1,72 +1,2 @@ -package com.lucdotdev.hover_ussd; - - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; - - - -import com.hover.sdk.api.HoverParameters; - -import java.util.HashMap; -import java.util.Map; - - - -public class HoverUssdApi { - - - private Activity activity; - private Context context; - - - - public HoverUssdApi(Activity activity, Context context) { - this.activity = activity; - - this.context = context; - } - - public void sendUssd(String action_id, - HashMap extra, - String theme, - String header, - String initialProcessingMessage, - int finalMsgDisplayTime, - boolean showUserStepDescriptions) { - - final HoverParameters.Builder builder = new HoverParameters.Builder(context).request(action_id); - - if (extra != null) { - if (!extra.isEmpty()) { - for (Map.Entry entry : extra.entrySet()) { - builder.extra(entry.getKey(), entry.getValue()); - } - } - } - - - if (theme != null) { - int id = context.getResources().getIdentifier(theme, "style", context.getPackageName()); - builder.style(id); - - } - if (header != null) { - builder.setHeader(header); - } - if (initialProcessingMessage != null) { - builder.initialProcessingMessage(initialProcessingMessage); - } - - - builder.showUserStepDescriptions(showUserStepDescriptions); - builder.finalMsgDisplayTime(finalMsgDisplayTime); - - Intent buildIntent = builder.buildIntent(); - - activity.startActivityForResult(buildIntent, 0); - - - } -} \ No newline at end of file +package com.lucdotdev.hover_ussd;public class HoverUssdApi { +} diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index 72e75cd..800deb2 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -1,224 +1,38 @@ -package com.lucdotdev.hover_ussd; - -import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.AssetFileDescriptor; -import android.content.res.AssetManager; -import android.graphics.drawable.Drawable; -import android.os.Build; - +package android.src.main.java.com.lucdotdev.hover_ussd; import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; - -import com.hover.sdk.api.Hover; -import com.hover.sdk.transactions.Transaction; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.embedding.engine.plugins.activity.ActivityAware; -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; -import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.PluginRegistry; - - -/** - * HoverUssdPlugin - */ -public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener, EventChannel.StreamHandler { - - - - private MethodChannel channel; - private Activity activity; - - - private EventChannel eventChannel; - private EventChannel.EventSink eventSink; - private BroadcastReceiver smsReceiver; - - - - @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - - - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "HoverUssdChannel"); - eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "TransactionEvent"); - - eventChannel.setStreamHandler(this); - channel.setMethodCallHandler(this); - } - - private String intentNullAwareString(Intent intent, String name) { - return intent.hasExtra(name) ? intent.getStringExtra(name) : ""; - } - - - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - HoverUssdApi hoverUssdApi = new HoverUssdApi(activity, activity.getApplicationContext()); - switch (call.method) { - case "Initialize": - - Hover.initialize(activity); - if (call.argument("branding") != null && call.argument("logo") != null) { - String[] parts = ((String) call.argument("logo")).split("/"); - String resourceType = parts[0]; - String resourceName = parts[1]; - - int id =activity.getApplicationContext().getResources().getIdentifier(resourceName, resourceType, activity.getApplicationContext().getPackageName()); - Hover.setBranding((String) call.argument("branding"), id, activity.getApplicationContext()); - } - break; - case "HoverStartATransaction": - Map resultJson = new HashMap<>(); - resultJson.put("state", "ussdLoading"); - eventSink.success(resultJson); - hoverUssdApi.sendUssd( - (String) call.argument("actionId"), - call.hasArgument("extras") ? - (HashMap) Objects.requireNonNull(call.argument("extras")) - : new HashMap(), - call.hasArgument("theme") ? - (String) call.argument("theme") : - "", - call.hasArgument("header") ? - (String) call.argument("header") - : "", - call.hasArgument("initialProcessingMessage") ? - (String) call.argument("initialProcessingMessage") - : "", - call.hasArgument("finalMsgDisplayTime") ? - (int) call.argument("finalMsgDisplayTime") - : 5000, - false - - - ); - break; - default: - result.notImplemented(); - - } - - } - - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); - eventChannel.setStreamHandler(null); - - } - - @Override - public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { - activity = binding.getActivity(); - - binding.addActivityResultListener(this); - - - smsReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Map result = new HashMap<>(); - - result.put("state", "smsParsed"); - - result.put("action_id", intentNullAwareString(intent, "action_id")); - result.put("response_message", intentNullAwareString(intent, "response_message")); - result.put("status", intentNullAwareString(intent, "status")); - result.put("status_meaning", intentNullAwareString(intent, "status_meaning")); - result.put("status_description", intentNullAwareString(intent, "status_description")); - result.put("uuid", intentNullAwareString(intent, "uuid")); - result.put("im_hni", intentNullAwareString(intent, "im_hni")); - result.put("environment", intent.getIntExtra("environment", 0)); - result.put("request_timestamp", intent.getIntExtra("request_timestamp", 0)); - result.put("response_timestamp", intent.getIntExtra("response_timestamp", 0)); - - result.put("matched_parser_id", intentNullAwareString(intent, "matched_parser_id")); - result.put("messagetype", intentNullAwareString(intent, "messagetype")); - result.put("message_sender", intentNullAwareString(intent, "message_sender")); - result.put("regex", intentNullAwareString(intent, "regex")); - - - eventSink.success(result); - } - }; - - - activity.registerReceiver(smsReceiver - , new IntentFilter("com.lucdotdev.hover_ussd.CONFIRMED_TRANSACTION")); - - } - - @Override - public void onDetachedFromActivityForConfigChanges() { - activity = null; - } - - @Override - public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { - activity = binding.getActivity(); - } - - @Override - public void onDetachedFromActivity() { - activity = null; - } - - - @Override - public boolean onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == 0 && resultCode == Activity.RESULT_OK) { - String uuid = data.hasExtra("uuid") ? data.getStringExtra("uuid") : ""; - - Map result = new HashMap<>(); - result.put("state", "ussdSucceded"); - result.put("uuid", uuid); - if (data.hasExtra("session_messages")) { - String[] sessionMessages = data.getStringArrayExtra("session_messages"); - result.put("ussdSessionMessages", sessionMessages); - } - - eventSink.success(result); - - return true; - - } - else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED) { - Map result = new HashMap<>(); - result.put("state", "ussdFailed"); - if (data != null) { - result.put("errorMessage", data.getStringExtra("error")); - } - eventSink.success(result); - - return true; - } - return false; - } - - @Override - public void onListen(Object arguments, EventChannel.EventSink events) { - eventSink = events; - } - - @Override - public void onCancel(Object arguments) { - eventSink = null; - } - -} \ No newline at end of file +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; + +/** HoverUssdPlugin */ +public class HoverUssdPlugin implements FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private MethodChannel channel; + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "hover_ussd"); + channel.setMethodCallHandler(this); + } + + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + if (call.method.equals("getPlatformVersion")) { + result.success("Android " + android.os.Build.VERSION.RELEASE); + } else { + result.notImplemented(); + } + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + channel.setMethodCallHandler(null); + } +} diff --git a/android/src/test/java/com/lucdotdev/hover_ussd/HoverUssdPluginTest.java b/android/src/test/java/com/lucdotdev/hover_ussd/HoverUssdPluginTest.java new file mode 100644 index 0000000..e4bb12b --- /dev/null +++ b/android/src/test/java/com/lucdotdev/hover_ussd/HoverUssdPluginTest.java @@ -0,0 +1,29 @@ +package android.src.test.java.com.lucdotdev.hover_ussd; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import org.junit.Test; + +/** + * This demonstrates a simple unit test of the Java portion of this plugin's implementation. + * + * Once you have built the plugin's example app, you can run these tests from the command + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or + * you can run them directly from IDEs that support JUnit such as Android Studio. + */ + +public class HoverUssdPluginTest { + @Test + public void onMethodCall_getPlatformVersion_returnsExpectedValue() { + HoverUssdPlugin plugin = new HoverUssdPlugin(); + + final MethodCall call = new MethodCall("getPlatformVersion", null); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + plugin.onMethodCall(call, mockResult); + + verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE); + } +} diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index 9d532b1..0000000 --- a/example/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json diff --git a/example/.metadata b/example/.metadata deleted file mode 100644 index d06ed58..0000000 --- a/example/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: bf9f3a3dcfea3022f9cf2dfc3ab10b120b48b19d - channel: master - -project_type: app diff --git a/example/README.md b/example/README.md deleted file mode 100644 index b2749d4..0000000 --- a/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# hover_ussd_example - -Demonstrates how to use the hover_ussd plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/example/android/.gitignore b/example/android/.gitignore deleted file mode 100644 index 0a741cb..0000000 --- a/example/android/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties diff --git a/example/android/.project b/example/android/.project deleted file mode 100644 index c82e66c..0000000 --- a/example/android/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - android - Project android_ created by Buildship. - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - - org.eclipse.buildship.core.gradleprojectnature - - - - 1630668570900 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - diff --git a/example/android/.settings/org.eclipse.buildship.core.prefs b/example/android/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index df7d455..0000000 --- a/example/android/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -arguments= -auto.sync=false -build.scans.enabled=false -connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) -connection.project.dir= -eclipse.preferences.version=1 -gradle.user.home= -java.home=C\:/Program Files/AdoptOpenJDK/jdk-11.0.10.9-hotspot -jvm.arguments= -offline.mode=false -override.workspace.settings=true -show.console.view=true -show.executions.view=true diff --git a/example/android/app/.classpath b/example/android/app/.classpath deleted file mode 100644 index 4a04201..0000000 --- a/example/android/app/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/example/android/app/.project b/example/android/app/.project deleted file mode 100644 index 3fd00ca..0000000 --- a/example/android/app/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - app - Project app created by Buildship. - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.buildship.core.gradleprojectnature - - - - 1630668570914 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - diff --git a/example/android/app/.settings/org.eclipse.buildship.core.prefs b/example/android/app/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index b1886ad..0000000 --- a/example/android/app/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,2 +0,0 @@ -connection.project.dir=.. -eclipse.preferences.version=1 diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index 6fadaac..0000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -1,54 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 29 - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.lucdotdev.hover_ussd_example" - minSdkVersion 18 - targetSdkVersion 29 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 97e0455..0000000 --- a/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 24cb535..0000000 --- a/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/example/android/app/src/main/java/com/lucdotdev/hover_ussd_example/MainActivity.java b/example/android/app/src/main/java/com/lucdotdev/hover_ussd_example/MainActivity.java deleted file mode 100644 index 6692794..0000000 --- a/example/android/app/src/main/java/com/lucdotdev/hover_ussd_example/MainActivity.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.lucdotdev.hover_ussd_example; - -import io.flutter.embedding.android.FlutterActivity; - -public class MainActivity extends FlutterActivity { -} diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f..0000000 --- a/example/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index bba3712..0000000 --- a/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 97e0455..0000000 --- a/example/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index e0d7ae2..0000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -buildscript { - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - } -} - -allprojects { - repositories { - google() - jcenter() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/example/android/gradle.properties b/example/android/gradle.properties deleted file mode 100644 index 38c8d45..0000000 --- a/example/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true -android.useAndroidX=true -android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 296b146..0000000 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jun 23 08:50:38 CEST 2017 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index 44e62bc..0000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/ios/Flutter/Generated.xcconfig b/example/ios/Flutter/Generated.xcconfig deleted file mode 100644 index 08f6844..0000000 --- a/example/ios/Flutter/Generated.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -// This is a generated file; do not edit or check into version control. -FLUTTER_ROOT=C:\flutter -FLUTTER_APPLICATION_PATH=C:\code\current\hover_ussd\example -COCOAPODS_PARALLEL_CODE_SIGN=true -FLUTTER_TARGET=lib\main.dart -FLUTTER_BUILD_DIR=build -FLUTTER_BUILD_NAME=1.0.0 -FLUTTER_BUILD_NUMBER=1 -EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 -DART_OBFUSCATION=false -TRACK_WIDGET_CREATION=false -TREE_SHAKE_ICONS=false -PACKAGE_CONFIG=.packages diff --git a/example/ios/Flutter/flutter_export_environment.sh b/example/ios/Flutter/flutter_export_environment.sh deleted file mode 100644 index 987b36b..0000000 --- a/example/ios/Flutter/flutter_export_environment.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=C:\flutter" -export "FLUTTER_APPLICATION_PATH=C:\code\current\hover_ussd\example" -export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=lib\main.dart" -export "FLUTTER_BUILD_DIR=build" -export "FLUTTER_BUILD_NAME=1.0.0" -export "FLUTTER_BUILD_NUMBER=1" -export "DART_OBFUSCATION=false" -export "TRACK_WIDGET_CREATION=false" -export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.packages" diff --git a/example/ios/Runner/GeneratedPluginRegistrant.h b/example/ios/Runner/GeneratedPluginRegistrant.h deleted file mode 100644 index 7a89092..0000000 --- a/example/ios/Runner/GeneratedPluginRegistrant.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GeneratedPluginRegistrant_h -#define GeneratedPluginRegistrant_h - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface GeneratedPluginRegistrant : NSObject -+ (void)registerWithRegistry:(NSObject*)registry; -@end - -NS_ASSUME_NONNULL_END -#endif /* GeneratedPluginRegistrant_h */ diff --git a/example/ios/Runner/GeneratedPluginRegistrant.m b/example/ios/Runner/GeneratedPluginRegistrant.m deleted file mode 100644 index efe65ec..0000000 --- a/example/ios/Runner/GeneratedPluginRegistrant.m +++ /dev/null @@ -1,14 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#import "GeneratedPluginRegistrant.h" - -@implementation GeneratedPluginRegistrant - -+ (void)registerWithRegistry:(NSObject*)registry { -} - -@end diff --git a/example/lib/main.dart b/example/lib/main.dart deleted file mode 100644 index 9cb22e8..0000000 --- a/example/lib/main.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; - -import 'package:hover_ussd/hover_ussd.dart'; - -void main() { - WidgetsFlutterBinding.ensureInitialized(); - HoverUssd.initialize( - branding: 'Hover Ussd Example', logo: "mipmap/ic_launcher"); - runApp(App()); -} - -class App extends StatelessWidget { - const App({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: MyApp(), - ); - } -} - -class MyApp extends StatefulWidget { - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - final HoverUssd _hoverUssd = HoverUssd(); - - late final StreamSubscription transactionListening; - @override - void initState() { - transactionListening = _hoverUssd.getUssdTransactionState!.listen((event) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(event.toMap().toString()))); - }); - super.initState(); - } - - @override - void dispose() { - transactionListening.cancel(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Hover Ussd Example'), - ), - body: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - TextButton( - onPressed: () { - _hoverUssd.startTransaction( - actionId: "c6e45e62", - extras: {"price": "4000"}, - theme: "myHoverTheme", - header: "Hover Ussd Example", - showUserStepDescriptions: true); - }, - child: Text("Start Transaction"), - ), - StreamBuilder( - stream: _hoverUssd.getUssdTransactionState, - builder: (BuildContext context, snapshot) { - if (snapshot.data is SmsParsed) { - return Text( - "Sms parsed : \n" + snapshot.data!.toMap().toString()); - } - - if (snapshot.data is UssdSucceded) { - return Text("Ussd Succeded : \n" + - snapshot.data!.toMap().toString()); - } - if (snapshot.data is UssdLoading) { - return Text("loading..."); - } - if (snapshot.data is UssdFailed) { - return Text( - "Ussd Failed : \n" + snapshot.data!.toMap().toString()); - } - if (snapshot.data is EmptyState) { - return Text("Empty State"); - } - - return Text("No state"); - }, - ), - ], - ), - ], - ), - ); - } -} diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index 46b8712..0000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,161 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.8.1" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.3" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - hover_ussd: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "2.0.0" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.10" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.7.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.2" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" -sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml deleted file mode 100644 index ecd2757..0000000 --- a/example/pubspec.yaml +++ /dev/null @@ -1,72 +0,0 @@ -name: hover_ussd_example -description: Demonstrates how to use the hover_ussd plugin. - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -environment: - sdk: '>=2.12.0 <3.0.0' - - -dependencies: - flutter: - sdk: flutter - - hover_ussd: - # When depending on this package from a real application you should use: - # hover_ussd: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. - path: ../ - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.3 - -dev_dependencies: - flutter_test: - sdk: flutter - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index 737318f..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,12 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; - -import 'package:hover_ussd_example/main.dart'; - -void main() {} diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart deleted file mode 100644 index c204179..0000000 --- a/lib/hover_ussd.dart +++ /dev/null @@ -1,340 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/services.dart'; - -class Transaction { - final String? id; - final String? actionId; - final String? uuid; - final String? status; - final String? category; - final String? userMessage; - final String? networkHni; - final String? inputExtras; - final String? parsedVariables; - final List? ussdMessages; - final List? enteredValues; - final List? smsHits; - final List? smsMisses; - final List? logMessages; - final List? matchedParsers; - final int? reqTimestamp; - final int? updatedTimestamp; - - Transaction( - {this.uuid, - this.id, - this.actionId, - this.status, - this.category, - this.userMessage, - this.networkHni, - this.inputExtras, - this.parsedVariables, - this.ussdMessages, - this.enteredValues, - this.smsHits, - this.smsMisses, - this.logMessages, - this.matchedParsers, - this.reqTimestamp, - this.updatedTimestamp}); - - factory Transaction.fromMap(Map map) { - return Transaction( - uuid: map['uuid'], - id: map['id'], - actionId: map['actionId'], - status: map['status'], - category: map['category'], - userMessage: map['userMessage'], - networkHni: map['networkHni'], - inputExtras: map['inputExtras'], - parsedVariables: map['parsedVariables'], - ussdMessages: map['ussdMessages'], - enteredValues: map['enteredValues'], - smsHits: map['smsHits'], - smsMisses: map['smsMisses'], - logMessages: map['logMessages'], - matchedParsers: map['matchedParsers'], - reqTimestamp: map['reqTimestamp'], - updatedTimestamp: map['updatedTimestamp'], - ); - } -} - -abstract class TransactionState { - TransactionState(); - - Map toMap(); -} - -/// when the message(sms) is successfully parsed -class SmsParsed extends TransactionState { - /// Unique Identifier for the transaction - final String? uuid; - - /// The action id from out supported operators page - final String? actionId; - - /// Full message used for parsing - final String? responseMessage; - - /// “pending”, “failed” or “succeeded” - final String? status; - - /// What you specified for the latest matched parser or one of the default failed cases above - final String? statusMeaning; - - /// Message you specified for the latest matched parser - final String? statusDescription; - - /// Unique identifier for the parser which matched, causing this transaction to update - final int? matchedParserId; - - /// “ussd” or “sms” - final String? messagetype; - - /// If SMS, the sender id from the parser form, null if USSD - final String? messageSender; - - /// Regular expression you specified in the parser form - final String? regex; - - /// The Home Network Identifier (MCC + MNC) of the SIM used - final String? simHni; - - /// 0 for normal, 1 for debug, 2 for test - final int? environment; - - /// Time user initiated transaction (Unix time) - final int? requestTimestamp; - - /// Time at which the transaction last updated (SMS arrival or USSD finished) - final int? updateTimestamp; - - /// (depreciated) Same as updated_timestamp - final int? responseTimestamp; - - /// A HashMap object of all the extras you passed in using .extra(key, value) - final Map? inputExtras; - - /// A HashMap object of all named groups parsed out of the response message based on your regex - final Map? parsedVariables; - - /// Array of all USSD session messages in order encountered - final List? sessionMessages; - - SmsParsed( - {this.uuid, - this.actionId, - this.responseMessage, - this.status, - this.statusMeaning, - this.statusDescription, - this.matchedParserId, - this.messagetype, - this.messageSender, - this.regex, - this.simHni, - this.environment, - this.requestTimestamp, - this.updateTimestamp, - this.responseTimestamp, - this.inputExtras, - this.parsedVariables, - this.sessionMessages}); - @override - Map toMap() { - return { - 'uuid': uuid, - 'action_id': actionId, - 'response_message': responseMessage, - 'status': status, - 'status_meaning': statusMeaning, - 'status_description': statusDescription, - 'matched_parser_id': matchedParserId, - 'messagetype': messagetype, - 'message_sender': messageSender, - 'regex': regex, - 'sim_hni': simHni, - 'environment': environment, - 'request_timestamp': requestTimestamp, - 'update_timestamp': updateTimestamp, - 'response_timestamp': responseTimestamp, - 'input_extras': inputExtras, - 'parsed_variables': parsedVariables, - 'session_messages': sessionMessages, - }; - } - - factory SmsParsed.fromMap(Map json) { - return SmsParsed( - uuid: json['uuid'] as String?, - sessionMessages: json['session_messages'] as List?, - inputExtras: json['input_extras'] as Map?, - parsedVariables: json['parsed_variables'] as Map?, - responseTimestamp: json['response_timestamp'] as int?, - updateTimestamp: json['update_timestamp'] as int?, - requestTimestamp: json['request_timestamp'] as int?, - environment: json['environment'] as int?, - simHni: json['sim_hni'] as String?, - regex: json['regex'] as String?, - messageSender: json['message_sender'] as String?, - messagetype: json['messagetype'] as String?, - matchedParserId: json['matched_parser_id'] as int?, - statusDescription: json['status_description'] as String?, - statusMeaning: json['status_meaning'] as String?, - actionId: json['action_id'] as String?, - responseMessage: json['response_message'] as String?, - status: json['status'] as String?, - ); - } -} - -/// when ussd session run succesfully -class UssdSucceded extends TransactionState { - /// Unique Identifier for the transaction - final String? uuid; - - /// The action id from out supported operators page - final String? actionId; - - /// Full message used for parsing - final String? responseMessage; - - UssdSucceded({this.uuid, this.actionId, this.responseMessage}); - @override - Map toMap() { - return { - 'uuid': uuid, - 'action_id': actionId, - 'response_message': responseMessage, - }; - } - - factory UssdSucceded.fromMap(Map json) { - return UssdSucceded( - uuid: json['uuid'] as String?, - actionId: json['action_id'] as String?, - responseMessage: json['response_message'] as String?, - ); - } -} - -/// when the ussd code failed; this can be caused by user -/// dissmiss or request refuse -class UssdFailed extends TransactionState { - /// error message if ussd call failed - final String? errorMessage; - - UssdFailed({this.errorMessage}); - - factory UssdFailed.fromMap(Map json) { - return UssdFailed(errorMessage: json["errorMessage"]); - } - - @override - Map toMap() { - return {"errorMessage": errorMessage}; - } -} - -class UssdLoading extends TransactionState { - @override - Map toMap() { - throw UnimplementedError(); - } -} - -class EmptyState extends TransactionState { - @override - Map toMap() { - throw UnimplementedError(); - } -} - -class HoverUssd { - static const MethodChannel _methodChannel = MethodChannel('HoverUssdChannel'); - static const EventChannel _eventChannel = EventChannel('TransactionEvent'); - - static Future initialize({String? branding, String? logo}) async { - await _methodChannel.invokeMethod("Initialize", { - "branding": branding ?? "Flutter App", - "logo": logo ?? "mipmap/ic_launcher" - }); - } - - Stream? _onTransactionStateChanged; - - /// when you call send ussd, automatically the ussd procces begin - /// and the permission is autaumatically handled - /// this method came with 7 params - /// [actionId] is the id for the action you trying to call - /// see - /// [extras] is the extras variable which came with action id , you can - /// optional: if you want to customize the ussd theme then you can add a theme at - /// your style.xml and then add the style string on [theme] param - /// see for other params - Future startTransaction( - {required String actionId, - Map? extras, - String? theme, - String? header, - String? initialProcessingMessage, - int? finalMsgDisplayTime, - bool? showUserStepDescriptions}) async => - await _methodChannel.invokeMethod("HoverStartATransaction", { - "actionId": actionId, - "extras": extras ?? {}, - "theme": theme, - "header": header, - "initialProcessingMessage": initialProcessingMessage, - "finalMsgDisplayTime": finalMsgDisplayTime ?? 5000 - }); - - Future> getAllTransactions() async { - List> result = - await _methodChannel.invokeMethod("HoverStartATransaction"); - List transctions = []; - for (final item in result) { - transctions.add(Transaction()); - } - return transctions; - } - - /// this is for getting response of the current ussd session - Stream? get getUssdTransactionState { - if (_onTransactionStateChanged == null) { - _onTransactionStateChanged = - _eventChannel.receiveBroadcastStream().map((dynamic event) { - try { - print(event); - TransactionState transactionState = _parseTransactionState( - event['state'], Map.from(event)); - return transactionState; - } catch (e) { - print(e); - return EmptyState(); - } - }); - } - return _onTransactionStateChanged; - } - - TransactionState _parseTransactionState( - String state, Map result) { - switch (state) { - case "smsParsed": - return SmsParsed.fromMap(result); - case "ussdSucceded": - return UssdSucceded.fromMap(result); - case "ussdFailed": - return UssdFailed.fromMap(result); - case "ussdLoading": - return UssdLoading(); - default: - return EmptyState(); - } - } -} diff --git a/test/hover_ussd_test.dart b/test/hover_ussd_test.dart index c916de9..aa30917 100644 --- a/test/hover_ussd_test.dart +++ b/test/hover_ussd_test.dart @@ -1,23 +1,29 @@ -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hover_ussd/hover_ussd.dart'; +import 'package:hover_ussd/hover_ussd_platform_interface.dart'; +import 'package:hover_ussd/hover_ussd_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -void main() { - const MethodChannel channel = MethodChannel('hover_ussd'); +class MockHoverUssdPlatform + with MockPlatformInterfaceMixin + implements HoverUssdPlatform { - TestWidgetsFlutterBinding.ensureInitialized(); + @override + Future getPlatformVersion() => Future.value('42'); +} - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return '42'; - }); - }); +void main() { + final HoverUssdPlatform initialPlatform = HoverUssdPlatform.instance; - group("Test Plugin", () { - test("Initialization", () {}); + test('$MethodChannelHoverUssd is the default instance', () { + expect(initialPlatform, isInstanceOf()); }); - tearDown(() { - channel.setMockMethodCallHandler(null); + test('getPlatformVersion', () async { + HoverUssd hoverUssdPlugin = HoverUssd(); + MockHoverUssdPlatform fakePlatform = MockHoverUssdPlatform(); + HoverUssdPlatform.instance = fakePlatform; + + expect(await hoverUssdPlugin.getPlatformVersion(), '42'); }); } From 00784de6e61c6614f077dd3aead1937f06ace296 Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Thu, 7 Mar 2024 21:14:29 +0200 Subject: [PATCH 26/36] candidate 2 --- .idea/misc.xml | 4 +- .../shelved.patch | 329 ++++++++++ .idea/workspace.xml | 120 +++- android/.gitignore | 2 + android/.idea/.gitignore | 3 + android/.idea/.name | 1 + android/.idea/compiler.xml | 6 + android/.idea/gradle.xml | 18 + android/.idea/migrations.xml | 10 + android/.idea/misc.xml | 9 + android/.idea/vcs.xml | 6 + android/build.gradle | 11 +- android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61608 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + android/gradlew | 244 +++++++ android/gradlew.bat | 92 +++ android/settings.gradle | 2 - android/src/main/AndroidManifest.xml | 1 + .../lucdotdev/hover_ussd/HoverUssdApi.java | 128 +++- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 327 +++++++++- .../hover_ussd/HoverUssdPluginTest.java | 11 - example/.gitignore | 44 ++ example/.metadata | 45 ++ example/README.md | 16 + example/analysis_options.yaml | 28 + example/android/.gitignore | 13 + example/android/app/build.gradle | 70 ++ .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 37 ++ .../app/FlutterMultiDexApplication.java | 25 + .../com/example/example/MainActivity.kt | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + example/android/build.gradle | 36 + example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + example/android/settings.gradle | 20 + .../plugin_integration_test.dart | 25 + example/ios/.gitignore | 34 + example/ios/Flutter/AppFrameworkInfo.plist | 26 + example/ios/Flutter/Debug.xcconfig | 1 + example/ios/Flutter/Release.xcconfig | 1 + example/ios/Runner.xcodeproj/project.pbxproj | 614 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + example/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 ++++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 ++ example/ios/Runner/Base.lproj/Main.storyboard | 26 + example/ios/Runner/Info.plist | 49 ++ example/ios/Runner/Runner-Bridging-Header.h | 1 + example/ios/RunnerTests/RunnerTests.swift | 12 + example/lib/main.dart | 128 ++++ example/pubspec.lock | 267 ++++++++ example/pubspec.yaml | 85 +++ example/test/widget_test.dart | 26 + hover_ussd.iml | 10 + lib/hover_ussd.dart | 4 + lib/hover_ussd_plugin.dart | 106 +++ lib/models/hover_action.dart | 48 ++ lib/models/transaction.dart | 62 ++ lib/models/transaction_state.dart | 191 ++++++ pubspec.lock | 136 ++-- pubspec.yaml | 23 +- test/hover_ussd_test.dart | 16 +- 98 files changed, 3849 insertions(+), 138 deletions(-) create mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35_[Default_Changelist]/shelved.patch create mode 100644 android/.idea/.gitignore create mode 100644 android/.idea/.name create mode 100644 android/.idea/compiler.xml create mode 100644 android/.idea/gradle.xml create mode 100644 android/.idea/migrations.xml create mode 100644 android/.idea/misc.xml create mode 100644 android/.idea/vcs.xml create mode 100644 android/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100644 android/gradlew create mode 100644 android/gradlew.bat create mode 100644 example/.gitignore create mode 100644 example/.metadata create mode 100644 example/README.md create mode 100644 example/analysis_options.yaml create mode 100644 example/android/.gitignore create mode 100644 example/android/app/build.gradle create mode 100644 example/android/app/src/debug/AndroidManifest.xml create mode 100644 example/android/app/src/main/AndroidManifest.xml create mode 100644 example/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java create mode 100644 example/android/app/src/main/kotlin/com/example/example/MainActivity.kt create mode 100644 example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/values-night/styles.xml create mode 100644 example/android/app/src/main/res/values/styles.xml create mode 100644 example/android/app/src/profile/AndroidManifest.xml create mode 100644 example/android/build.gradle create mode 100644 example/android/gradle.properties create mode 100644 example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 example/android/settings.gradle create mode 100644 example/integration_test/plugin_integration_test.dart create mode 100644 example/ios/.gitignore create mode 100644 example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 example/ios/Flutter/Debug.xcconfig create mode 100644 example/ios/Flutter/Release.xcconfig create mode 100644 example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 example/ios/Runner/AppDelegate.swift create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 example/ios/Runner/Info.plist create mode 100644 example/ios/Runner/Runner-Bridging-Header.h create mode 100644 example/ios/RunnerTests/RunnerTests.swift create mode 100644 example/lib/main.dart create mode 100644 example/pubspec.lock create mode 100644 example/pubspec.yaml create mode 100644 example/test/widget_test.dart create mode 100644 lib/hover_ussd.dart create mode 100644 lib/hover_ussd_plugin.dart create mode 100644 lib/models/hover_action.dart create mode 100644 lib/models/transaction.dart create mode 100644 lib/models/transaction_state.dart diff --git a/.idea/misc.xml b/.idea/misc.xml index ac5bd6f..7a8e207 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,6 @@ - + + \ No newline at end of file diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35_[Default_Changelist]/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35_[Default_Changelist]/shelved.patch new file mode 100644 index 0000000..f59518c --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35_[Default_Changelist]/shelved.patch @@ -0,0 +1,329 @@ +Index: lib/hover_ussd.dart +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>import 'dart:async';\r\nimport 'package:flutter/services.dart';\r\n\r\nenum TransactionState {\r\n // when the ussd code run succesfully\r\n\r\n succesfull,\r\n // when the ussd code failed; this can be caused by user\r\n // dissmiss or request refuse\r\n failed,\r\n // this is when hover failed to dowload the list of your\r\n // action , usualy when the app is not connected\r\n actionDowaloadFailed\r\n}\r\n\r\nclass HoverUssd {\r\n static const MethodChannel _methodChannel = MethodChannel('HoverUssdChannel');\r\n static const EventChannel _eventChannel = EventChannel('TransactionEvent');\r\n\r\n static Future initialize({String? branding, String? logo}) async {\r\n await _methodChannel.invokeMethod(\"Initialize\",\r\n {\"branding\": branding ?? \"Flutter App\", \"logo\": logo ?? \"ic_launcher\"});\r\n }\r\n\r\n Stream? _onTransactionStateChanged;\r\n\r\n // when you call send ussd, automatically the ussd procces begin\r\n // and the permission is autaumatically handle\r\n // this method came with 3 params\r\n // @actionId is the id for the action you trying to call\r\n // cc: https://docs.usehover.com/ussd\r\n // @extras is the extras variable which came with action id , you can\r\n // convigure tha on : https://www.usehover.com/\r\n // optional: if you want to customize the ussd theme then you can add a theme at\r\n // your style.xml and then add the style string on @theme param\r\n Future sendUssd(\r\n {required String actionId,\r\n Map? extras,\r\n String? theme}) async =>\r\n await _methodChannel.invokeMethod(\"HoverStartATransaction\",\r\n {\"actionId\": actionId, \"extras\": extras ?? {}, \"theme\": theme});\r\n\r\n // this is for getting response of the current ussd session\r\n\r\n Stream? get getUssdTransactionState {\r\n if (_onTransactionStateChanged == null) {\r\n _onTransactionStateChanged = _eventChannel\r\n .receiveBroadcastStream()\r\n .map((dynamic event) => _parseTransactionState(event));\r\n }\r\n return _onTransactionStateChanged;\r\n }\r\n\r\n TransactionState _parseTransactionState(String? state) {\r\n switch (state) {\r\n case \"success\":\r\n return TransactionState.succesfull;\r\n case \"failed\":\r\n return TransactionState.failed;\r\n case \"actionDownloadFailed\":\r\n return TransactionState.actionDowaloadFailed;\r\n default:\r\n throw ArgumentError('$state');\r\n }\r\n }\r\n}\r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart +--- a/lib/hover_ussd.dart (revision ef28cc9dd4019341ab6a14710a361884e6bd2ec0) ++++ b/lib/hover_ussd.dart (date 1709327262151) +@@ -24,13 +24,13 @@ + + Stream? _onTransactionStateChanged; + +- // when you call send ussd, automatically the ussd procces begin +- // and the permission is autaumatically handle ++ // when you call send ussd, automatically the ussd process begin ++ // and the permission is automatically handle + // this method came with 3 params + // @actionId is the id for the action you trying to call + // cc: https://docs.usehover.com/ussd + // @extras is the extras variable which came with action id , you can +- // convigure tha on : https://www.usehover.com/ ++ // configure tha on : https://www.usehover.com/ + // optional: if you want to customize the ussd theme then you can add a theme at + // your style.xml and then add the style string on @theme param + Future sendUssd( +Index: pubspec.lock +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+># Generated by pub\r\n# See https://dart.dev/tools/pub/glossary#lockfile\r\npackages:\r\n async:\r\n dependency: transitive\r\n description:\r\n name: async\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"2.8.1\"\r\n boolean_selector:\r\n dependency: transitive\r\n description:\r\n name: boolean_selector\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"2.1.0\"\r\n characters:\r\n dependency: transitive\r\n description:\r\n name: characters\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.1.0\"\r\n charcode:\r\n dependency: transitive\r\n description:\r\n name: charcode\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.3.1\"\r\n clock:\r\n dependency: transitive\r\n description:\r\n name: clock\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.1.0\"\r\n collection:\r\n dependency: transitive\r\n description:\r\n name: collection\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.15.0\"\r\n fake_async:\r\n dependency: transitive\r\n description:\r\n name: fake_async\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.2.0\"\r\n flutter:\r\n dependency: \"direct main\"\r\n description: flutter\r\n source: sdk\r\n version: \"0.0.0\"\r\n flutter_test:\r\n dependency: \"direct dev\"\r\n description: flutter\r\n source: sdk\r\n version: \"0.0.0\"\r\n matcher:\r\n dependency: transitive\r\n description:\r\n name: matcher\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"0.12.10\"\r\n meta:\r\n dependency: \"direct main\"\r\n description:\r\n name: meta\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.7.0\"\r\n path:\r\n dependency: transitive\r\n description:\r\n name: path\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.8.0\"\r\n sky_engine:\r\n dependency: transitive\r\n description: flutter\r\n source: sdk\r\n version: \"0.0.99\"\r\n source_span:\r\n dependency: transitive\r\n description:\r\n name: source_span\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.8.1\"\r\n stack_trace:\r\n dependency: transitive\r\n description:\r\n name: stack_trace\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.10.0\"\r\n stream_channel:\r\n dependency: transitive\r\n description:\r\n name: stream_channel\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"2.1.0\"\r\n string_scanner:\r\n dependency: transitive\r\n description:\r\n name: string_scanner\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.1.0\"\r\n term_glyph:\r\n dependency: transitive\r\n description:\r\n name: term_glyph\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.2.0\"\r\n test_api:\r\n dependency: transitive\r\n description:\r\n name: test_api\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"0.4.2\"\r\n typed_data:\r\n dependency: transitive\r\n description:\r\n name: typed_data\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.3.0\"\r\n vector_math:\r\n dependency: transitive\r\n description:\r\n name: vector_math\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"2.1.0\"\r\nsdks:\r\n dart: \">=2.12.0 <3.0.0\"\r\n flutter: \">=1.20.0\"\r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/pubspec.lock b/pubspec.lock +--- a/pubspec.lock (revision ef28cc9dd4019341ab6a14710a361884e6bd2ec0) ++++ b/pubspec.lock (date 1709327201783) +@@ -5,51 +5,50 @@ + dependency: transitive + description: + name: async +- url: "https://pub.dartlang.org" ++ sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" ++ url: "https://pub.dev" + source: hosted +- version: "2.8.1" ++ version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector +- url: "https://pub.dartlang.org" ++ sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" ++ url: "https://pub.dev" + source: hosted +- version: "2.1.0" ++ version: "2.1.1" + characters: + dependency: transitive + description: + name: characters +- url: "https://pub.dartlang.org" ++ sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" ++ url: "https://pub.dev" + source: hosted +- version: "1.1.0" +- charcode: +- dependency: transitive +- description: +- name: charcode +- url: "https://pub.dartlang.org" +- source: hosted +- version: "1.3.1" ++ version: "1.3.0" + clock: + dependency: transitive + description: + name: clock +- url: "https://pub.dartlang.org" ++ sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf ++ url: "https://pub.dev" + source: hosted +- version: "1.1.0" ++ version: "1.1.1" + collection: + dependency: transitive + description: + name: collection +- url: "https://pub.dartlang.org" ++ sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 ++ url: "https://pub.dev" + source: hosted +- version: "1.15.0" ++ version: "1.17.2" + fake_async: + dependency: transitive + description: + name: fake_async +- url: "https://pub.dartlang.org" ++ sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" ++ url: "https://pub.dev" + source: hosted +- version: "1.2.0" ++ version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter +@@ -64,23 +63,34 @@ + dependency: transitive + description: + name: matcher +- url: "https://pub.dartlang.org" ++ sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" ++ url: "https://pub.dev" + source: hosted +- version: "0.12.10" ++ version: "0.12.16" ++ material_color_utilities: ++ dependency: transitive ++ description: ++ name: material_color_utilities ++ sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" ++ url: "https://pub.dev" ++ source: hosted ++ version: "0.5.0" + meta: + dependency: "direct main" + description: + name: meta +- url: "https://pub.dartlang.org" ++ sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" ++ url: "https://pub.dev" + source: hosted +- version: "1.7.0" ++ version: "1.9.1" + path: + dependency: transitive + description: + name: path +- url: "https://pub.dartlang.org" ++ sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" ++ url: "https://pub.dev" + source: hosted +- version: "1.8.0" ++ version: "1.8.3" + sky_engine: + dependency: transitive + description: flutter +@@ -90,58 +100,66 @@ + dependency: transitive + description: + name: source_span +- url: "https://pub.dartlang.org" ++ sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" ++ url: "https://pub.dev" + source: hosted +- version: "1.8.1" ++ version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace +- url: "https://pub.dartlang.org" ++ sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 ++ url: "https://pub.dev" + source: hosted +- version: "1.10.0" ++ version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel +- url: "https://pub.dartlang.org" ++ sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" ++ url: "https://pub.dev" + source: hosted +- version: "2.1.0" ++ version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner +- url: "https://pub.dartlang.org" ++ sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" ++ url: "https://pub.dev" + source: hosted +- version: "1.1.0" ++ version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph +- url: "https://pub.dartlang.org" ++ sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 ++ url: "https://pub.dev" + source: hosted +- version: "1.2.0" ++ version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api +- url: "https://pub.dartlang.org" ++ sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" ++ url: "https://pub.dev" + source: hosted +- version: "0.4.2" +- typed_data: ++ version: "0.6.0" ++ vector_math: + dependency: transitive + description: +- name: typed_data +- url: "https://pub.dartlang.org" ++ name: vector_math ++ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" ++ url: "https://pub.dev" + source: hosted +- version: "1.3.0" +- vector_math: ++ version: "2.1.4" ++ web: + dependency: transitive + description: +- name: vector_math +- url: "https://pub.dartlang.org" ++ name: web ++ sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 ++ url: "https://pub.dev" + source: hosted +- version: "2.1.0" ++ version: "0.1.4-beta" + sdks: +- dart: ">=2.12.0 <3.0.0" ++ dart: ">=3.1.0-185.0.dev <4.0.0" + flutter: ">=1.20.0" +Index: .idea/workspace.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n 1596904412123\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/.idea/workspace.xml b/.idea/workspace.xml +--- a/.idea/workspace.xml (revision ef28cc9dd4019341ab6a14710a361884e6bd2ec0) ++++ b/.idea/workspace.xml (date 1709327262323) +@@ -1,34 +1,51 @@ + + ++ ++ + + +- +- +- +- +- +- ++ ++ ++ ++ ++ + + ++ ++ + + + ++ ++ + + + +- +- +- +- +- ++ ++ + + + +Index: .idea/misc.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\r\n\r\n \r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/.idea/misc.xml b/.idea/misc.xml +--- a/.idea/misc.xml (revision ef28cc9dd4019341ab6a14710a361884e6bd2ec0) ++++ b/.idea/misc.xml (date 1709353258729) +@@ -1,4 +1,6 @@ +- + + ++ ++ + +\ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 9ad56c0..0955904 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,35 +1,125 @@ + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + - - - - - - + + + + + + @@ -43,14 +133,4 @@ - - - - - - - - - - \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore index 161bdcd..b84dbef 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -7,3 +7,5 @@ /build /captures .cxx + +/tempsLibs diff --git a/android/.idea/.gitignore b/android/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/android/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/android/.idea/.name b/android/.idea/.name new file mode 100644 index 0000000..fd21e78 --- /dev/null +++ b/android/.idea/.name @@ -0,0 +1 @@ +hover_ussd \ No newline at end of file diff --git a/android/.idea/compiler.xml b/android/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/android/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml new file mode 100644 index 0000000..aeac74f --- /dev/null +++ b/android/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/android/.idea/migrations.xml b/android/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/android/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml new file mode 100644 index 0000000..8978d23 --- /dev/null +++ b/android/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/android/.idea/vcs.xml b/android/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/android/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 12e4bf3..7a1ec8e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,12 +1,12 @@ -package android - group 'com.lucdotdev.hover_ussd' -version '1.0' +version '2.1.0' buildscript { repositories { google() mavenCentral() + maven { url "https://maven.usehover.com/snapshots" } + } dependencies { @@ -18,6 +18,8 @@ rootProject.allprojects { repositories { google() mavenCentral() + maven { url "https://maven.usehover.com/snapshots" } + } } @@ -40,10 +42,13 @@ android { } dependencies { + compileOnly files('tempsLibs/flutter.jar') testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.0.0' + implementation 'com.hover:android-sdk:2.0.0-beta02-noSMS' } + testOptions { unitTests.all { testLogging { diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..ccebba7710deaf9f98673a68957ea02138b60d0a GIT binary patch literal 61608 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 literal 0 HcmV?d00001 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..42defcc --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew new file mode 100644 index 0000000..79a61d4 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/settings.gradle b/android/settings.gradle index b70c813..b4034f0 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,3 +1 @@ -package android - rootProject.name = 'hover_ussd' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index ed8aca5..8bd0db9 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,4 @@ + diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index 1ff8dec..608c0d3 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -1,2 +1,128 @@ -package com.lucdotdev.hover_ussd;public class HoverUssdApi { +package com.lucdotdev.hover_ussd; + + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import com.hover.sdk.actions.HoverAction; +import com.hover.sdk.api.Hover; +import com.hover.sdk.api.HoverParameters; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.flutter.plugin.common.EventChannel; + +public class HoverUssdApi { + + + private final Activity activity; + private final EventChannel.EventSink eventSink; + + public HoverUssdApi(Activity activity, EventChannel.EventSink eventSink) { + this.activity = activity; + this.eventSink = eventSink; + } + + public void initialize(String branding, String logo, String notificationLogo, Hover.DownloadListener downloadListener) { + Hover.initialize(activity.getApplicationContext(), false, downloadListener); + if (branding != null && logo != null && notificationLogo != null) { + Context context = activity.getApplicationContext(); + int logoResourceId = getResourceId(context, logo); + int notificationLogoResourceId = getResourceId(context, notificationLogo); + if (logoResourceId == 0) { + Log.e("HOVER_USSD_PLUGIN", ":LOGO NOT FOUND"); + } else { + Hover.setBranding(branding, logoResourceId, notificationLogoResourceId, activity.getApplicationContext()); + } + } + } + + public boolean hasAllPerms() { + return Hover.hasAllPerms(activity.getApplicationContext()); + } + + + // get all actions from hover and convert to an list of array map + + + public void getAllActions(Hover.DownloadListener actionsDownloadListener) { + Hover.updateActionConfigs(actionsDownloadListener, activity.getApplicationContext()); + } + + @Deprecated() + public void getAllTransaction() { + } + + private int getResourceId(Context context, String resourceName) { + try { + Class res = R.drawable.class; + Field field = res.getField(resourceName); + return field.getInt(null); + } catch (Exception e) { + e.printStackTrace(); + return 0; // Return 0 if resource ID is not found + } + } + + public void sendUssd(String action_id, + HashMap extra, + String theme, + String header, + String initialProcessingMessage, + boolean showUserStepDescriptions, + int finalMsgDisplayTime + + ) { + + + final HoverParameters.Builder builder = new HoverParameters.Builder(activity.getApplicationContext()).request(action_id); + + if (extra != null) { + if (!extra.isEmpty()) { + for (Map.Entry entry : extra.entrySet()) { + builder.extra(entry.getKey(), entry.getValue()); + } + } + } + + + if (theme != null) { + int id = activity.getBaseContext().getResources().getIdentifier(theme, "style", activity.getApplicationContext().getPackageName()); + builder.style(id); + + } + if (header != null) { + builder.setHeader(header); + } + if (initialProcessingMessage != null) { + builder.initialProcessingMessage(initialProcessingMessage); + } + + + builder.showUserStepDescriptions(showUserStepDescriptions); + if (finalMsgDisplayTime != 0) { + builder.finalMsgDisplayTime(finalMsgDisplayTime); + } + + try { + Intent buildIntent = builder.buildIntent(); + + activity.startActivityForResult(buildIntent, 0); + } catch (Exception e) { + + Map result = new HashMap<>(); + + result.put("state", "ussdFailed"); + result.put("errorMessage", e); + + eventSink.success(result); + + } + + } } diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index 800deb2..20adadd 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -1,38 +1,301 @@ -package android.src.main.java.com.lucdotdev.hover_ussd; +package com.lucdotdev.hover_ussd; + + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + import androidx.annotation.NonNull; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.hover.sdk.actions.HoverAction; +import com.hover.sdk.api.Hover; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; - -/** HoverUssdPlugin */ -public class HoverUssdPlugin implements FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private MethodChannel channel; - - @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "hover_ussd"); - channel.setMethodCallHandler(this); - } - - @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { - if (call.method.equals("getPlatformVersion")) { - result.success("Android " + android.os.Build.VERSION.RELEASE); - } else { - result.notImplemented(); - } - } - - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); - } -} +import io.flutter.plugin.common.PluginRegistry; + +/** + * HoverUssdPlugin + */ + + +public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener, EventChannel.StreamHandler { + + + private Activity activity; + + + private EventChannel.EventSink eventSink; + private BroadcastReceiver smsReceiver; + + private HoverUssdApi hoverUssdApi; + + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + + + MethodChannel channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "HoverUssdChannel"); + EventChannel eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "TransactionEvent"); + + eventChannel.setStreamHandler(this); + channel.setMethodCallHandler(this); + + + smsReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Map result = new HashMap<>(); + result.put("state", "smsParsed"); + result.put("action_id", intentNullAwareString(intent, "action_id")); + result.put("response_message", intentNullAwareString(intent, "response_message")); + result.put("status", intentNullAwareString(intent, "status")); + result.put("status_meaning", intentNullAwareString(intent, "status_meaning")); + result.put("status_description", intentNullAwareString(intent, "status_description")); + result.put("uuid", intentNullAwareString(intent, "uuid")); + result.put("im_hni", intentNullAwareString(intent, "im_hni")); + result.put("environment", intent.getIntExtra("environment", 0)); + result.put("request_timestamp", intent.getIntExtra("request_timestamp", 0)); + result.put("response_timestamp", intent.getIntExtra("response_timestamp", 0)); + + result.put("matched_parser_id", intentNullAwareString(intent, "matched_parser_id")); + result.put("messagetype", intentNullAwareString(intent, "messagetype")); + result.put("message_sender", intentNullAwareString(intent, "message_sender")); + result.put("regex", intentNullAwareString(intent, "regex")); + + + eventSink.success(result); + } + }; + + + } + + private String intentNullAwareString(Intent intent, String name) { + return intent.hasExtra(name) ? intent.getStringExtra(name) : ""; + } + + private Map actionToMap(HoverAction hoverAction) { + Map map = new HashMap<>(); + map.put("id", hoverAction.id); + map.put("public_id", hoverAction.public_id); + map.put("name", hoverAction.name); + map.put("channel_id", hoverAction.channel_id); + map.put("network_name", hoverAction.network_name); + map.put("hni_list", hoverAction.hni_list); // Convert JSONArray to String + map.put("country_alpha2", hoverAction.country_alpha2); + map.put("root_code", hoverAction.root_code); + map.put("transport_type", hoverAction.transport_type); + map.put("transaction_type", hoverAction.transaction_type); + map.put("custom_steps", hoverAction.custom_steps); // Convert JSONArray to String + map.put("from_institution_id", hoverAction.from_institution_id); + map.put("from_institution_name", hoverAction.from_institution_name); + map.put("from_institution_logo", hoverAction.from_institution_logo); + map.put("to_institution_id", hoverAction.to_institution_id); + map.put("to_institution_name", hoverAction.to_institution_name); + map.put("to_institution_logo", hoverAction.to_institution_logo); + map.put("to_country_alpha2", hoverAction.to_country_alpha2); + map.put("tags_list", hoverAction.tags_list); // Convert JSONArray to String + map.put("created_timestamp", hoverAction.created_timestamp); + map.put("updated_timestamp", hoverAction.updated_timestamp); + map.put("bounty_amount", hoverAction.bounty_amount); + map.put("bounty_is_open", hoverAction.bounty_is_open); + map.put("is_ready", hoverAction.is_ready); + map.put("required_params", hoverAction.required_params); // Convert JSONObject to String + map.put("output_params", hoverAction.output_params); // Convert JSONObject to String + map.put("bonus_percent", hoverAction.bonus_percent); + map.put("bonus_message", hoverAction.bonus_message); + + return map; + + } + + + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { + hoverUssdApi = new HoverUssdApi(activity, eventSink); + + switch (call.method) { + case "Initialize": + hoverUssdApi.initialize(call.argument("branding"), call.argument("logo"), call.argument("notificationLogo"), new Hover.DownloadListener() { + @Override + public void onError(String s) { + result.success(null); + + } + + @Override + public void onSuccess(ArrayList arrayList) { + ArrayList> actionsList = new ArrayList<>(); + + for (HoverAction hoverAction : arrayList) { + + actionsList.add(actionToMap(hoverAction)); + } + + result.success(actionsList); + } + } + ); + break; + + case "HasAllPermissions": + result.success(hoverUssdApi.hasAllPerms()); + break; + case "getAllActions": + hoverUssdApi.getAllActions(new Hover.DownloadListener() { + @Override + public void onError(String s) { + result.success(null); + + } + + @Override + public void onSuccess(ArrayList arrayList) { + ArrayList> actionsList = new ArrayList<>(); + + for (HoverAction hoverAction : arrayList) { + + actionsList.add(actionToMap(hoverAction)); + } + + result.success(actionsList); + } + }); + + case "HoverStartATransaction": + Map resultJson = new HashMap<>(); + resultJson.put("state", "ussdLoading"); + eventSink.success(resultJson); + hoverUssdApi.sendUssd( + (String) call.argument("actionId"), + call.hasArgument("extras") ? + Objects.requireNonNull(call.argument("extras")) + : new HashMap(), + call.hasArgument("theme") ? + (String) call.argument("theme") : + "", + call.hasArgument("header") ? + (String) call.argument("header") + : "", + call.hasArgument("initialProcessingMessage") ? + (String) call.argument("initialProcessingMessage") + : "", + false, + call.hasArgument("finalMsgDisplayTime") ? (int) call.argument("finalMsgDisplayTime") : 5000 + + + ); + + LocalBroadcastManager.getInstance(activity.getBaseContext()).registerReceiver(smsReceiver, new IntentFilter(activity.getPackageName() + ".SMS_MISS")); + + + break; + default: + result.notImplemented(); + + } + + } + + + @Override + public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { + activity = binding.getActivity(); + binding.addActivityResultListener(this); + + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + activity = null; + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + + eventSink.endOfStream(); + + + } + + @Override + public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { + activity = binding.getActivity(); + } + + @Override + public void onDetachedFromActivity() { + LocalBroadcastManager.getInstance(activity.getBaseContext()).unregisterReceiver(smsReceiver); + activity = null; + } + + + @Override + public boolean onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == 0 && resultCode == Activity.RESULT_OK) { + String uuid = data.hasExtra("uuid") ? data.getStringExtra("uuid") : ""; + + Map result = new HashMap<>(); + result.put("state", "ussdSucceded"); + result.put("uuid", uuid); + if (data.hasExtra("session_messages")) { + String[] sessionMessages = data.getStringArrayExtra("session_messages"); + result.put("ussdSessionMessages", sessionMessages); + } + + eventSink.success(result); + + return true; + + } else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED) { + Map result = new HashMap<>(); + result.put("state", "ussdFailed"); + if (data != null) { + result.put("errorMessage", data.getStringExtra("error")); + } + eventSink.success(result); + + return false; + } else { + Map result = new HashMap<>(); + + + result.put("state", "ussdFailed"); + if (data != null) { + result.put("errorMessage", data.getStringExtra("error")); + } + eventSink.success(result); + return false; + } + + } + + + + @Override + public void onListen(Object arguments, EventChannel.EventSink events) { + if (eventSink == null) { + eventSink = events; + } + } + + @Override + public void onCancel(Object arguments) { + eventSink = null; + } + +} \ No newline at end of file diff --git a/android/src/test/java/com/lucdotdev/hover_ussd/HoverUssdPluginTest.java b/android/src/test/java/com/lucdotdev/hover_ussd/HoverUssdPluginTest.java index e4bb12b..a5b0c88 100644 --- a/android/src/test/java/com/lucdotdev/hover_ussd/HoverUssdPluginTest.java +++ b/android/src/test/java/com/lucdotdev/hover_ussd/HoverUssdPluginTest.java @@ -1,10 +1,5 @@ package android.src.test.java.com.lucdotdev.hover_ussd; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; import org.junit.Test; /** @@ -18,12 +13,6 @@ public class HoverUssdPluginTest { @Test public void onMethodCall_getPlatformVersion_returnsExpectedValue() { - HoverUssdPlugin plugin = new HoverUssdPlugin(); - - final MethodCall call = new MethodCall("getPlatformVersion", null); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - plugin.onMethodCall(call, mockResult); - verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE); } } diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..24476c5 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/.metadata b/example/.metadata new file mode 100644 index 0000000..b48f68e --- /dev/null +++ b/example/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "2f708eb8396e362e280fac22cf171c2cb467343c" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + - platform: android + create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + - platform: ios + create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + - platform: linux + create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + - platform: macos + create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + - platform: web + create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + - platform: windows + create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..ba937ff --- /dev/null +++ b/example/README.md @@ -0,0 +1,16 @@ +# hover_ussd_example + +Demonstrates how to use the hover_ussd plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle new file mode 100644 index 0000000..3e8bb68 --- /dev/null +++ b/example/android/app/build.gradle @@ -0,0 +1,70 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +android { + namespace "com.example.example" + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } + +} + +flutter { + source '../..' +} + +dependencies { + +} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..03954a6 --- /dev/null +++ b/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java b/example/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java new file mode 100644 index 0000000..752fc18 --- /dev/null +++ b/example/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java @@ -0,0 +1,25 @@ +// Generated file. +// +// If you wish to remove Flutter's multidex support, delete this entire file. +// +// Modifications to this file should be done in a copy under a different name +// as this file may be regenerated. + +package io.flutter.app; + +import android.app.Application; +import android.content.Context; +import androidx.annotation.CallSuper; +import androidx.multidex.MultiDex; + +/** + * Extension of {@link android.app.Application}, adding multidex support. + */ +public class FlutterMultiDexApplication extends Application { + @Override + @CallSuper + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + MultiDex.install(this); + } +} diff --git a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt new file mode 100644 index 0000000..e793a00 --- /dev/null +++ b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle new file mode 100644 index 0000000..c586514 --- /dev/null +++ b/example/android/build.gradle @@ -0,0 +1,36 @@ +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + maven { url "https://maven.usehover.com/snapshots" } + } + + + dependencies { + + classpath 'com.android.tools.build:gradle:7.3.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + + } +} + +allprojects { + repositories { + google() + mavenCentral() + maven { url "https://maven.usehover.com/snapshots" } + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3c472b9 --- /dev/null +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle new file mode 100644 index 0000000..55c4ca8 --- /dev/null +++ b/example/android/settings.gradle @@ -0,0 +1,20 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + } + settings.ext.flutterSdkPath = flutterSdkPath() + + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + + plugins { + id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false + } +} + +include ":app" + +apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/integration_test/plugin_integration_test.dart b/example/integration_test/plugin_integration_test.dart new file mode 100644 index 0000000..94da938 --- /dev/null +++ b/example/integration_test/plugin_integration_test.dart @@ -0,0 +1,25 @@ +// This is a basic Flutter integration test. +// +// Since integration tests run in a full Flutter application, they can interact +// with the host side of a plugin implementation, unlike Dart unit tests. +// +// For more information about Flutter integration tests, please see +// https://docs.flutter.dev/cookbook/testing/integration/introduction + + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'package:hover_ussd/hover_ussd.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('getPlatformVersion test', (WidgetTester tester) async { + final HoverUssd plugin = HoverUssd(); + + // The version string depends on the host platform running the test, so + // just assert that some non-empty string is returned. + + }); +} diff --git a/example/ios/.gitignore b/example/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..9625e10 --- /dev/null +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 11.0 + + diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/example/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..75c0e50 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,614 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807E294A63A400263BE5 /* Frameworks */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1430; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..87131a0 --- /dev/null +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +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. \ No newline at end of file diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist new file mode 100644 index 0000000..5458fc4 --- /dev/null +++ b/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/example/ios/RunnerTests/RunnerTests.swift b/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 0000000..2c6aad8 --- /dev/null +++ b/example/lib/main.dart @@ -0,0 +1,128 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:hover_ussd/hover_ussd.dart'; +import 'package:hover_ussd/models/transaction_state.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Hover USSD Example', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: MyAppHomePage(), + ); + } +} + +class MyAppHomePage extends StatefulWidget { + @override + _MyAppHomePageState createState() => _MyAppHomePageState(); +} + +class _MyAppHomePageState extends State { + final HoverUssd _hoverUssd = HoverUssd(); + late StreamSubscription _transactionListening; + + Future initHover(BuildContext context) async { + List? actions = await _hoverUssd.initialize( + branding: 'Hover Ussd Example', logo: "mipmap/ic_launcher"); + + if (actions == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Action download failed"))); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Action download successfully"))); + } + } + + Future getAllActions() async { + List? action = await _hoverUssd.getActions(); + + if (action == null) { + print("Action download failed"); + } else { + print(action); + } + } + + @override + void initState() { + initHover(context); + getAllActions(); + + _transactionListening = + _hoverUssd.getUssdTransactionState().listen((event) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(event.toMap().toString()))); + }); + super.initState(); + } + + @override + void dispose() { + _transactionListening.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Hover Ussd Example'), + ), + body: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: () { + _hoverUssd.startTransaction( + actionId: "93c9da25", + extras: {}, + theme: "myHoverTheme", + header: "Hover Ussd Example", + showUserStepDescriptions: true); + }, + child: const Text("Start Transaction"), + ), + StreamBuilder( + stream: _hoverUssd.getUssdTransactionState(), + builder: (BuildContext context, snapshot) { + if (snapshot.data is SmsParsed) { + return Text( + "Sms parsed : \n${(snapshot.data as SmsParsed).toMap()}"); + } + + if (snapshot.data is UssdSucceeded) { + return Text("Ussd Succeded : \n${(snapshot.data as UssdSucceeded).toMap()}"); + } + if (snapshot.data is UssdLoading) { + return const Text("loading..."); + } + if (snapshot.data is UssdFailed) { + return Text("Ussd Failed : \n${(snapshot.data as UssdFailed).toMap()}"); + } + if (snapshot.data is EmptyState) { + return const Text("Empty State"); + } + + return const Text("No state"); + }, + ), + ], + ), + ], + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 0000000..f9a7818 --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,267 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" + source: hosted + version: "1.17.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + hover_ussd: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "2.0.0" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + url: "https://pub.dev" + source: hosted + version: "11.7.1" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + url: "https://pub.dev" + source: hosted + version: "3.0.2" +sdks: + dart: ">=3.1.3 <4.0.0" + flutter: ">=3.3.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..a5d7044 --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,85 @@ +name: hover_ussd_example +description: Demonstrates how to use the hover_ussd plugin. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=3.1.3 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + hover_ussd: + # When depending on this package from a real application you should use: + # hover_ussd: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + integration_test: + sdk: flutter + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart new file mode 100644 index 0000000..8d49d7f --- /dev/null +++ b/example/test/widget_test.dart @@ -0,0 +1,26 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:hover_ussd_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => widget is Text && + widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/hover_ussd.iml b/hover_ussd.iml index 73e7ebd..ca846be 100644 --- a/hover_ussd.iml +++ b/hover_ussd.iml @@ -11,7 +11,17 @@ + + + + + + + + + + diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart new file mode 100644 index 0000000..3a9177d --- /dev/null +++ b/lib/hover_ussd.dart @@ -0,0 +1,4 @@ +export 'hover_ussd_plugin.dart'; +export 'models/transaction.dart'; +export 'models/transaction_state.dart'; +export 'models/hover_action.dart'; \ No newline at end of file diff --git a/lib/hover_ussd_plugin.dart b/lib/hover_ussd_plugin.dart new file mode 100644 index 0000000..5ad3a5e --- /dev/null +++ b/lib/hover_ussd_plugin.dart @@ -0,0 +1,106 @@ +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:hover_ussd/models/hover_action.dart'; + +import 'models/transaction.dart'; +import 'models/transaction_state.dart'; + +class HoverUssd { + static const MethodChannel _methodChannel = + MethodChannel('HoverUssdChannel'); + static const EventChannel _eventChannel = + EventChannel('TransactionEvent'); + + Stream? _onTransactionStateChanged; + + /// Initialize HoverUssd with branding and logo information. + Future?> initialize( + {String? branding, String? logo}) async { + List? hoverActions = await _methodChannel.invokeMethod( + "Initialize", + {"branding": branding ?? "Flutter App", "logo": logo ?? "ic_launcher"}); + + return hoverActions; + } + + /// Start a transaction with the provided parameters. + Future startTransaction({ + required String actionId, + Map? extras, + String? theme, + String? header, + String? initialProcessingMessage, + int finalMsgDisplayTime = 5000, bool? showUserStepDescriptions, + }) async { + await _methodChannel.invokeMethod("HoverStartATransaction", { + "actionId": actionId, + "extras": extras ?? {}, + "theme": theme, + "header": header, + "initialProcessingMessage": initialProcessingMessage, + "finalMsgDisplayTime": finalMsgDisplayTime, + }); + } + + /// Check if the app has all the necessary permissions. + Future hasAllPermissions() async { + return await _methodChannel.invokeMethod("HasAllPermissions"); + } + + /// Check if the app has downloaded actions. + Future hasActionsDownloaded() async { + return await _methodChannel.invokeMethod("HasActionsDownloaded"); + } + + /// Retrieve a list of available actions. + Future> getActions() async { + List> result = await _methodChannel.invokeListMethod( + "getAllActions") as List>; + List actions = result.map((item) => HoverAction.fromMap(item)).toList(); + return actions; + } + + /// Deprecated: Retrieve all transactions. + @Deprecated('As usehover v2') + Future> getAllTransactions() async { + List> result = await _methodChannel + .invokeListMethod("getAllTransaction") as List>; + List transactions = result.map((item) => Transaction.fromMap(item)).toList(); + return transactions; + } + + /// Get the stream of transaction states. + Stream getUssdTransactionState() { + if (_onTransactionStateChanged == null) { + _onTransactionStateChanged ??= _eventChannel + .receiveBroadcastStream() + .map((dynamic event) => _parseTransactionState( + event['state'], + Map.from(event), + )) + .asBroadcastStream(); + } + + return _onTransactionStateChanged!; + } + + /// Parse transaction state from received event. + TransactionState _parseTransactionState( + String state, + Map result, + ) { + switch (state) { + case "smsParsed": + return SmsParsed.fromMap(result); + case "ussdSucceded": + return UssdSucceeded.fromMap(result); + case "ussdFailed": + return UssdFailed.fromMap(result); + case "ussdLoading": + return UssdLoading(); + default: + return EmptyState(); + } + } +} \ No newline at end of file diff --git a/lib/models/hover_action.dart b/lib/models/hover_action.dart new file mode 100644 index 0000000..3444887 --- /dev/null +++ b/lib/models/hover_action.dart @@ -0,0 +1,48 @@ +class HoverAction { + String id; + String name; + String networkName; + String rootCode; + String transportType; + String country; + List steps; + List hnis; + + HoverAction({ + required this.id, + required this.name, + required this.networkName, + required this.rootCode, + required this.transportType, + required this.country, + required this.steps, + required this.hnis, + }); + + Map toMap() { + return { + 'id': id, + 'name': name, + 'networkName': networkName, + 'rootCode': rootCode, + 'transportType': transportType, + 'country': country, + 'steps': steps, + 'hnis': hnis, + }; + } + + //from map + factory HoverAction.fromMap(Map json) { + return HoverAction( + id: json['id'] as String, + name: json['name'] as String, + networkName: json['networkName'] as String, + rootCode: json['rootCode'] as String, + transportType: json['transportType'] as String, + country: json['country'] as String, + steps: json['steps'].cast(), + hnis: json['hnis'].cast(), + ); + } +} \ No newline at end of file diff --git a/lib/models/transaction.dart b/lib/models/transaction.dart new file mode 100644 index 0000000..18b9486 --- /dev/null +++ b/lib/models/transaction.dart @@ -0,0 +1,62 @@ +class Transaction { + final String? id; + final String? actionId; + final String? uuid; + final String? status; + final String? category; + final String? userMessage; + final String? networkHni; + final String? inputExtras; + final String? parsedVariables; + final List? ussdMessages; + final List? enteredValues; + final List? smsHits; + final List? smsMisses; + final List? logMessages; + final List? matchedParsers; + final int? reqTimestamp; + final int? updatedTimestamp; + + Transaction( + {this.uuid, + this.id, + this.actionId, + this.status, + this.category, + this.userMessage, + this.networkHni, + this.inputExtras, + this.parsedVariables, + this.ussdMessages, + this.enteredValues, + this.smsHits, + this.smsMisses, + this.logMessages, + this.matchedParsers, + this.reqTimestamp, + this.updatedTimestamp}); + + factory Transaction.fromMap(Map map) { + return Transaction( + uuid: map['uuid'], + id: map['id'], + actionId: map['actionId'], + status: map['status'], + category: map['category'], + userMessage: map['userMessage'], + networkHni: map['networkHni'], + inputExtras: map['inputExtras'], + parsedVariables: map['parsedVariables'], + ussdMessages: map['ussdMessages'], + enteredValues: map['enteredValues'], + smsHits: map['smsHits'], + smsMisses: map['smsMisses'], + logMessages: map['logMessages'], + matchedParsers: map['matchedParsers'], + reqTimestamp: map['reqTimestamp'], + updatedTimestamp: map['updatedTimestamp'], + ); + } +} + + diff --git a/lib/models/transaction_state.dart b/lib/models/transaction_state.dart new file mode 100644 index 0000000..6915873 --- /dev/null +++ b/lib/models/transaction_state.dart @@ -0,0 +1,191 @@ +abstract class TransactionState { + TransactionState(); + + Map toMap(); +} + +/// when the message(sms) is successfully parsed +class SmsParsed extends TransactionState { + /// Unique Identifier for the transaction + final String? uuid; + + /// The action id from out supported operators page + final String? actionId; + + /// Full message used for parsing + final String? responseMessage; + + /// “pending”, “failed” or “succeeded” + final String? status; + + /// What you specified for the latest matched parser or one of the default failed cases above + final String? statusMeaning; + + /// Message you specified for the latest matched parser + final String? statusDescription; + + /// Unique identifier for the parser which matched, causing this transaction to update + final int? matchedParserId; + + /// “ussd” or “sms” + final String? messagetype; + + /// If SMS, the sender id from the parser form, null if USSD + final String? messageSender; + + /// Regular expression you specified in the parser form + final String? regex; + + /// The Home Network Identifier (MCC + MNC) of the SIM used + final String? simHni; + + /// 0 for normal, 1 for debug, 2 for test + final int? environment; + + /// Time user initiated transaction (Unix time) + final int? requestTimestamp; + + /// Time at which the transaction last updated (SMS arrival or USSD finished) + final int? updateTimestamp; + + /// (depreciated) Same as updated_timestamp + final int? responseTimestamp; + + /// A HashMap object of all the extras you passed in using .extra(key, value) + final Map? inputExtras; + + /// A HashMap object of all named groups parsed out of the response message based on your regex + final Map? parsedVariables; + + /// Array of all USSD session messages in order encountered + final List? sessionMessages; + + SmsParsed( + {this.uuid, + this.actionId, + this.responseMessage, + this.status, + this.statusMeaning, + this.statusDescription, + this.matchedParserId, + this.messagetype, + this.messageSender, + this.regex, + this.simHni, + this.environment, + this.requestTimestamp, + this.updateTimestamp, + this.responseTimestamp, + this.inputExtras, + this.parsedVariables, + this.sessionMessages}); + @override + Map toMap() { + return { + 'uuid': uuid, + 'action_id': actionId, + 'response_message': responseMessage, + 'status': status, + 'status_meaning': statusMeaning, + 'status_description': statusDescription, + 'matched_parser_id': matchedParserId, + 'messagetype': messagetype, + 'message_sender': messageSender, + 'regex': regex, + 'sim_hni': simHni, + 'environment': environment, + 'request_timestamp': requestTimestamp, + 'update_timestamp': updateTimestamp, + 'response_timestamp': responseTimestamp, + 'input_extras': inputExtras, + 'parsed_variables': parsedVariables, + 'session_messages': sessionMessages, + }; + } + + factory SmsParsed.fromMap(Map json) { + return SmsParsed( + uuid: json['uuid'] as String?, + sessionMessages: json['session_messages'] as List?, + inputExtras: json['input_extras'] as Map?, + parsedVariables: json['parsed_variables'] as Map?, + responseTimestamp: json['response_timestamp'] as int?, + updateTimestamp: json['update_timestamp'] as int?, + requestTimestamp: json['request_timestamp'] as int?, + environment: json['environment'] as int?, + simHni: json['sim_hni'] as String?, + regex: json['regex'] as String?, + messageSender: json['message_sender'] as String?, + messagetype: json['messagetype'] as String?, + matchedParserId: json['matched_parser_id'] as int?, + statusDescription: json['status_description'] as String?, + statusMeaning: json['status_meaning'] as String?, + actionId: json['action_id'] as String?, + responseMessage: json['response_message'] as String?, + status: json['status'] as String?, + ); + } +} + +/// when ussd session run succesfully +class UssdSucceeded extends TransactionState { + /// Unique Identifier for the transaction + final String? uuid; + + /// The action id from out supported operators page + final String? actionId; + + /// Full message used for parsing + final String? responseMessage; + + UssdSucceeded({this.uuid, this.actionId, this.responseMessage}); + @override + Map toMap() { + return { + 'uuid': uuid, + 'action_id': actionId, + 'response_message': responseMessage, + }; + } + + factory UssdSucceeded.fromMap(Map json) { + return UssdSucceeded( + uuid: json['uuid'] as String?, + actionId: json['action_id'] as String?, + responseMessage: json['response_message'] as String?, + ); + } +} + +/// when the ussd code failed; this can be caused by user +/// dissmiss or request refuse +class UssdFailed extends TransactionState { + /// error message if ussd call failed + final String? errorMessage; + + UssdFailed({this.errorMessage}); + + factory UssdFailed.fromMap(Map json) { + return UssdFailed(errorMessage: json["errorMessage"]); + } + + @override + Map toMap() { + return {"errorMessage": errorMessage}; + } +} + +class UssdLoading extends TransactionState { + @override + Map toMap() { + return {}; + } +} + + +class EmptyState extends TransactionState { + @override + Map toMap() { + return {}; + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 4935c14..166d22c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,82 +5,116 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.8.1" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" + version: "1.3.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.2" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" source: hosted - version: "0.12.10" + version: "0.5.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.9.1" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.8.3" + plugin_platform_interface: + dependency: "direct main" + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" sky_engine: dependency: transitive description: flutter @@ -90,58 +124,66 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" source: hosted - version: "0.4.2" - typed_data: + version: "0.6.0" + vector_math: dependency: transitive description: - name: typed_data - url: "https://pub.dartlang.org" + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "1.3.0" - vector_math: + version: "2.1.4" + web: dependency: transitive description: - name: vector_math - url: "https://pub.dartlang.org" + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "0.1.4-beta" sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + dart: ">=3.1.3 <4.0.0" + flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 247bc52..132ff3b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,27 +7,33 @@ version: 2.0.0 homepage: https://github.com/lucdotdev/hover_ussd environment: - sdk: '>=2.12.0 <3.0.0' - flutter: ">=1.20.0" - + sdk: '>=3.1.3 <4.0.0' + flutter: '>=3.3.0' dependencies: flutter: sdk: flutter - + plugin_platform_interface: ^2.0.2 dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^2.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec -# The following section is specific to Flutter. +# The following section is specific to Flutter packages. flutter: # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' and Android 'package' identifiers should not ordinarily - # be modified. They are used by the tooling to maintain consistency when + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when # adding or updating assets for this project. plugin: platforms: @@ -44,7 +50,7 @@ flutter: # https://flutter.dev/assets-and-images/#from-packages # # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. + # https://flutter.dev/assets-and-images/#resolution-aware # To add custom fonts to your plugin package, add a fonts section here, # in this "flutter" section. Each entry in this list should have a @@ -65,3 +71,4 @@ flutter: # # For details regarding fonts in packages, see # https://flutter.dev/custom-fonts/#from-packages + diff --git a/test/hover_ussd_test.dart b/test/hover_ussd_test.dart index aa30917..ffb39c8 100644 --- a/test/hover_ussd_test.dart +++ b/test/hover_ussd_test.dart @@ -1,29 +1,19 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:hover_ussd/hover_ussd.dart'; -import 'package:hover_ussd/hover_ussd_platform_interface.dart'; -import 'package:hover_ussd/hover_ussd_method_channel.dart'; + import 'package:plugin_platform_interface/plugin_platform_interface.dart'; class MockHoverUssdPlatform with MockPlatformInterfaceMixin - implements HoverUssdPlatform { + { - @override - Future getPlatformVersion() => Future.value('42'); } void main() { - final HoverUssdPlatform initialPlatform = HoverUssdPlatform.instance; - test('$MethodChannelHoverUssd is the default instance', () { - expect(initialPlatform, isInstanceOf()); - }); test('getPlatformVersion', () async { HoverUssd hoverUssdPlugin = HoverUssd(); - MockHoverUssdPlatform fakePlatform = MockHoverUssdPlatform(); - HoverUssdPlatform.instance = fakePlatform; - - expect(await hoverUssdPlugin.getPlatformVersion(), '42'); +; }); } From 567549f5898cb100510d0f603792a3108ff6cb6a Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Sat, 9 Mar 2024 05:00:28 +0200 Subject: [PATCH 27/36] candidate 3 --- .../java/com/lucdotdev/hover_ussd/HoverUssdObjectToMap.java | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdObjectToMap.java diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdObjectToMap.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdObjectToMap.java new file mode 100644 index 0000000..515dfe8 --- /dev/null +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdObjectToMap.java @@ -0,0 +1,2 @@ +package com.lucdotdev.hover_ussd;public class HoverUssdObjectToMap { +} From 42cbcfa4cbd37f6e567f9fbb008ff1a93e40af78 Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Sat, 9 Mar 2024 05:00:40 +0200 Subject: [PATCH 28/36] candidate 4 --- android/build.gradle | 2 +- .../lucdotdev/hover_ussd/HoverUssdApi.java | 60 +++- .../hover_ussd/HoverUssdObjectToMap.java | 70 ++++- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 288 ++++++++---------- .../android/app/src/main/AndroidManifest.xml | 3 - example/lib/main.dart | 240 ++++++++++----- example/pubspec.lock | 86 +++--- lib/hover_ussd.dart | 3 +- lib/hover_ussd_plugin.dart | 127 ++++++-- lib/models/download_action_state.dart | 59 ++++ lib/models/hover_action.dart | 145 ++++++--- pubspec.lock | 66 ++-- 12 files changed, 780 insertions(+), 369 deletions(-) create mode 100644 lib/models/download_action_state.dart diff --git a/android/build.gradle b/android/build.gradle index 7a1ec8e..c48c773 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -45,7 +45,7 @@ android { compileOnly files('tempsLibs/flutter.jar') testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.0.0' - implementation 'com.hover:android-sdk:2.0.0-beta02-noSMS' + implementation 'com.hover:android-sdk:2.0.0-beta04' } diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index 608c0d3..b5ffcaf 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -9,8 +9,11 @@ import com.hover.sdk.actions.HoverAction; import com.hover.sdk.api.Hover; import com.hover.sdk.api.HoverParameters; +import com.hover.sdk.database.HoverRoomDatabase; +import com.hover.sdk.transactions.Transaction; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -23,21 +26,23 @@ public class HoverUssdApi { private final Activity activity; private final EventChannel.EventSink eventSink; + private final Context context; + public HoverUssdApi(Activity activity, EventChannel.EventSink eventSink) { this.activity = activity; this.eventSink = eventSink; + this.context = activity.getApplicationContext(); } - public void initialize(String branding, String logo, String notificationLogo, Hover.DownloadListener downloadListener) { - Hover.initialize(activity.getApplicationContext(), false, downloadListener); + public void initialize(String apiKey, String branding, String logo, String notificationLogo, Hover.DownloadListener downloadListener) { + Hover.initialize(context, apiKey, true, downloadListener); if (branding != null && logo != null && notificationLogo != null) { - Context context = activity.getApplicationContext(); int logoResourceId = getResourceId(context, logo); int notificationLogoResourceId = getResourceId(context, notificationLogo); if (logoResourceId == 0) { Log.e("HOVER_USSD_PLUGIN", ":LOGO NOT FOUND"); } else { - Hover.setBranding(branding, logoResourceId, notificationLogoResourceId, activity.getApplicationContext()); + Hover.setBranding(branding, logoResourceId, notificationLogoResourceId, context); } } } @@ -50,12 +55,40 @@ public boolean hasAllPerms() { // get all actions from hover and convert to an list of array map - public void getAllActions(Hover.DownloadListener actionsDownloadListener) { - Hover.updateActionConfigs(actionsDownloadListener, activity.getApplicationContext()); + public ArrayList> getAllActions() { + List actions = HoverRoomDatabase.getInstance(context).actionDao().getAll(); + ArrayList> mapActions = new ArrayList<>(); + + for (HoverAction action : actions) { + Map mapAction = HoverUssdObjectToMap.convertHoverActionToMap(action); + mapActions.add(mapAction); + } + + return mapActions; + } + + public void updateActionConfigs(Hover.DownloadListener actionDownloadListener) { + Hover.updateActionConfigs(actionDownloadListener, context); } - @Deprecated() - public void getAllTransaction() { + public void refreshActions(Hover.DownloadListener actionDownloadListener) { + Hover.updateActionConfigs(actionDownloadListener, context); + } + + + public ArrayList> getAllTransaction() { + + List transactions = HoverRoomDatabase.getInstance(context).transactionDao().getAll(); + ArrayList> mapTransactions = new ArrayList<>(); + + for (Transaction transaction : transactions) { + + Map mapTransaction = HoverUssdObjectToMap.convertTransactionToMap(transaction); + mapTransactions.add(mapTransaction); + } + + return mapTransactions; + } private int getResourceId(Context context, String resourceName) { @@ -80,7 +113,9 @@ public void sendUssd(String action_id, ) { - final HoverParameters.Builder builder = new HoverParameters.Builder(activity.getApplicationContext()).request(action_id); + final HoverParameters.Builder builder = new HoverParameters.Builder(context); + + builder.request(action_id); if (extra != null) { if (!extra.isEmpty()) { @@ -92,7 +127,7 @@ public void sendUssd(String action_id, if (theme != null) { - int id = activity.getBaseContext().getResources().getIdentifier(theme, "style", activity.getApplicationContext().getPackageName()); + int id = context.getResources().getIdentifier(theme, "style", context.getPackageName()); builder.style(id); } @@ -103,15 +138,15 @@ public void sendUssd(String action_id, builder.initialProcessingMessage(initialProcessingMessage); } - builder.showUserStepDescriptions(showUserStepDescriptions); + + if (finalMsgDisplayTime != 0) { builder.finalMsgDisplayTime(finalMsgDisplayTime); } try { Intent buildIntent = builder.buildIntent(); - activity.startActivityForResult(buildIntent, 0); } catch (Exception e) { @@ -119,7 +154,6 @@ public void sendUssd(String action_id, result.put("state", "ussdFailed"); result.put("errorMessage", e); - eventSink.success(result); } diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdObjectToMap.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdObjectToMap.java index 515dfe8..0c29f48 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdObjectToMap.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdObjectToMap.java @@ -1,2 +1,70 @@ -package com.lucdotdev.hover_ussd;public class HoverUssdObjectToMap { +package com.lucdotdev.hover_ussd; + +import com.hover.sdk.actions.HoverAction; +import com.hover.sdk.transactions.Transaction; + +import java.util.HashMap; +import java.util.Map; + +public class HoverUssdObjectToMap { + + public static Map convertHoverActionToMap(HoverAction hoverAction) { + Map map = new HashMap<>(); + map.put("id", hoverAction.id); + map.put("public_id", hoverAction.public_id); + map.put("name", hoverAction.name); + map.put("channel_id", hoverAction.channel_id); + map.put("network_name", hoverAction.network_name); + map.put("country_alpha2", hoverAction.country_alpha2); + map.put("root_code", hoverAction.root_code); + map.put("transport_type", hoverAction.transport_type); + map.put("transaction_type", hoverAction.transaction_type); + map.put("from_institution_id", hoverAction.from_institution_id); + map.put("from_institution_name", hoverAction.from_institution_name); + map.put("from_institution_logo", hoverAction.from_institution_logo); + map.put("to_institution_id", hoverAction.to_institution_id); + map.put("to_institution_name", hoverAction.to_institution_name); + map.put("to_institution_logo", hoverAction.to_institution_logo); + map.put("to_country_alpha2", hoverAction.to_country_alpha2); + map.put("created_timestamp", hoverAction.created_timestamp); + map.put("updated_timestamp", hoverAction.updated_timestamp); + map.put("bounty_amount", hoverAction.bounty_amount); + map.put("bounty_is_open", hoverAction.bounty_is_open); + map.put("is_ready", hoverAction.is_ready); + map.put("bonus_percent", hoverAction.bonus_percent); + map.put("bonus_message", hoverAction.bonus_message); + + // Ignoring 'parsers' field as it's not included in the map + + return map; + } + + public static Map convertTransactionToMap(Transaction transaction) { + Map map = new HashMap<>(); + + map.put("id", transaction.id); + map.put("channelId", transaction.channelId); + map.put("statusId", transaction.statusId); + map.put("env", transaction.env); + map.put("actionId", transaction.actionId); + map.put("actionName", transaction.actionName); + map.put("uuid", transaction.uuid); + map.put("status", transaction.status); + map.put("category", transaction.category); + map.put("userMessage", transaction.userMessage); + map.put("networkHni", transaction.networkHni); + map.put("myType", transaction.myType); + map.put("toInstitutionName", transaction.toInstitutionName); + map.put("fromInstitutionName", transaction.fromInstitutionName); + map.put("configVersion", transaction.configVersion); + map.put("sdkVersion", transaction.sdkVersion); + map.put("reqTimestamp", transaction.reqTimestamp); + map.put("updatedTimestamp", transaction.updatedTimestamp); + + + + return map; + } + } + diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index 20adadd..dcf4c83 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -7,7 +7,6 @@ import android.content.Intent; import android.content.IntentFilter; - import androidx.annotation.NonNull; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -32,13 +31,14 @@ */ -public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener, EventChannel.StreamHandler { +public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener{ private Activity activity; - private EventChannel.EventSink eventSink; + private EventChannel.EventSink transactionEventSink; + private EventChannel.EventSink actionDownloadEventSink; private BroadcastReceiver smsReceiver; private HoverUssdApi hoverUssdApi; @@ -47,39 +47,36 @@ public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChan @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - MethodChannel channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "HoverUssdChannel"); - EventChannel eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "TransactionEvent"); - - eventChannel.setStreamHandler(this); - channel.setMethodCallHandler(this); + EventChannel transactionEventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "TransactionEvent"); + EventChannel actionDownloadEventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "ActionDownloadEvent"); + transactionEventChannel.setStreamHandler(new EventChannel.StreamHandler() { + @Override + public void onListen(Object arguments, EventChannel.EventSink events) { + transactionEventSink = events; + } - smsReceiver = new BroadcastReceiver() { @Override - public void onReceive(Context context, Intent intent) { - Map result = new HashMap<>(); - result.put("state", "smsParsed"); - result.put("action_id", intentNullAwareString(intent, "action_id")); - result.put("response_message", intentNullAwareString(intent, "response_message")); - result.put("status", intentNullAwareString(intent, "status")); - result.put("status_meaning", intentNullAwareString(intent, "status_meaning")); - result.put("status_description", intentNullAwareString(intent, "status_description")); - result.put("uuid", intentNullAwareString(intent, "uuid")); - result.put("im_hni", intentNullAwareString(intent, "im_hni")); - result.put("environment", intent.getIntExtra("environment", 0)); - result.put("request_timestamp", intent.getIntExtra("request_timestamp", 0)); - result.put("response_timestamp", intent.getIntExtra("response_timestamp", 0)); + public void onCancel(Object arguments) { + } + }); + actionDownloadEventChannel.setStreamHandler(new EventChannel.StreamHandler() { + @Override + public void onListen(Object arguments, EventChannel.EventSink events) { + actionDownloadEventSink = events; + } - result.put("matched_parser_id", intentNullAwareString(intent, "matched_parser_id")); - result.put("messagetype", intentNullAwareString(intent, "messagetype")); - result.put("message_sender", intentNullAwareString(intent, "message_sender")); - result.put("regex", intentNullAwareString(intent, "regex")); + @Override + public void onCancel(Object arguments) { + } + }); + channel.setMethodCallHandler(this); + } - eventSink.success(result); - } - }; + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { } @@ -88,126 +85,101 @@ private String intentNullAwareString(Intent intent, String name) { return intent.hasExtra(name) ? intent.getStringExtra(name) : ""; } - private Map actionToMap(HoverAction hoverAction) { - Map map = new HashMap<>(); - map.put("id", hoverAction.id); - map.put("public_id", hoverAction.public_id); - map.put("name", hoverAction.name); - map.put("channel_id", hoverAction.channel_id); - map.put("network_name", hoverAction.network_name); - map.put("hni_list", hoverAction.hni_list); // Convert JSONArray to String - map.put("country_alpha2", hoverAction.country_alpha2); - map.put("root_code", hoverAction.root_code); - map.put("transport_type", hoverAction.transport_type); - map.put("transaction_type", hoverAction.transaction_type); - map.put("custom_steps", hoverAction.custom_steps); // Convert JSONArray to String - map.put("from_institution_id", hoverAction.from_institution_id); - map.put("from_institution_name", hoverAction.from_institution_name); - map.put("from_institution_logo", hoverAction.from_institution_logo); - map.put("to_institution_id", hoverAction.to_institution_id); - map.put("to_institution_name", hoverAction.to_institution_name); - map.put("to_institution_logo", hoverAction.to_institution_logo); - map.put("to_country_alpha2", hoverAction.to_country_alpha2); - map.put("tags_list", hoverAction.tags_list); // Convert JSONArray to String - map.put("created_timestamp", hoverAction.created_timestamp); - map.put("updated_timestamp", hoverAction.updated_timestamp); - map.put("bounty_amount", hoverAction.bounty_amount); - map.put("bounty_is_open", hoverAction.bounty_is_open); - map.put("is_ready", hoverAction.is_ready); - map.put("required_params", hoverAction.required_params); // Convert JSONObject to String - map.put("output_params", hoverAction.output_params); // Convert JSONObject to String - map.put("bonus_percent", hoverAction.bonus_percent); - map.put("bonus_message", hoverAction.bonus_message); - - return map; - - } - @Override public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - hoverUssdApi = new HoverUssdApi(activity, eventSink); - + hoverUssdApi = new HoverUssdApi(activity, transactionEventSink); switch (call.method) { case "Initialize": - hoverUssdApi.initialize(call.argument("branding"), call.argument("logo"), call.argument("notificationLogo"), new Hover.DownloadListener() { - @Override - public void onError(String s) { - result.success(null); - - } - - @Override - public void onSuccess(ArrayList arrayList) { - ArrayList> actionsList = new ArrayList<>(); - - for (HoverAction hoverAction : arrayList) { - - actionsList.add(actionToMap(hoverAction)); - } + hoverUssdApi.initialize(call.argument("apiKey"), call.argument("branding"), call.argument("logo"), call.argument("notificationLogo"), new Hover.DownloadListener() { + @Override + public void onError(String s) { + Map result = new HashMap<>(); + result.put("state", "actionDownloadFailed"); + result.put("error", "error"); + actionDownloadEventSink.success(result); + } - result.success(actionsList); - } - } - ); + @Override + public void onSuccess(ArrayList arrayList) { + Map result = new HashMap<>(); + result.put("state", "actionDownloaded"); + result.put("isDownloaded", true); + actionDownloadEventSink.success(result); + } + }); + break; - case "HasAllPermissions": result.success(hoverUssdApi.hasAllPerms()); break; case "getAllActions": - hoverUssdApi.getAllActions(new Hover.DownloadListener() { + result.success(hoverUssdApi.getAllActions()); + break; + case "getAllTransactions": + result.success(hoverUssdApi.getAllTransaction()); + break; + case "refreshActions": + hoverUssdApi.refreshActions(new Hover.DownloadListener() { @Override public void onError(String s) { - result.success(null); - + Map result = new HashMap<>(); + result.put("state", "actionDownloadFailed"); + result.put("error", "error"); + actionDownloadEventSink.success(result); } @Override public void onSuccess(ArrayList arrayList) { - ArrayList> actionsList = new ArrayList<>(); - - for (HoverAction hoverAction : arrayList) { - - actionsList.add(actionToMap(hoverAction)); - } - - result.success(actionsList); + Map result = new HashMap<>(); + result.put("state", "actionDownloaded"); + result.put("isDownloaded", true); + actionDownloadEventSink.success(result); } }); + break; case "HoverStartATransaction": Map resultJson = new HashMap<>(); resultJson.put("state", "ussdLoading"); - eventSink.success(resultJson); - hoverUssdApi.sendUssd( - (String) call.argument("actionId"), - call.hasArgument("extras") ? - Objects.requireNonNull(call.argument("extras")) - : new HashMap(), - call.hasArgument("theme") ? - (String) call.argument("theme") : - "", - call.hasArgument("header") ? - (String) call.argument("header") - : "", - call.hasArgument("initialProcessingMessage") ? - (String) call.argument("initialProcessingMessage") - : "", - false, - call.hasArgument("finalMsgDisplayTime") ? (int) call.argument("finalMsgDisplayTime") : 5000 - - - ); - - LocalBroadcastManager.getInstance(activity.getBaseContext()).registerReceiver(smsReceiver, new IntentFilter(activity.getPackageName() + ".SMS_MISS")); - + transactionEventSink.success(resultJson); + + try { + hoverUssdApi.sendUssd( + (String) call.argument("actionId"), + call.hasArgument("extras") ? + Objects.requireNonNull(call.argument("extras")) + : new HashMap(), + call.hasArgument("theme") ? + (String) call.argument("theme") : + "", + call.hasArgument("header") ? + (String) call.argument("header") + : "", + call.hasArgument("initialProcessingMessage") ? + (String) call.argument("initialProcessingMessage") + : "", + false, + call.hasArgument("finalMsgDisplayTime") ? (int) call.argument("finalMsgDisplayTime") : 5000); + } catch (Exception e) { + + Map resultError = new HashMap<>(); + resultError.put("state", "ussdFailed"); + + resultError.put("errorMessage", e.getMessage()); + + transactionEventSink.success(resultError); + + } + + result.success(true); break; default: result.notImplemented(); } + hoverUssdApi = null; } @@ -216,6 +188,32 @@ public void onSuccess(ArrayList arrayList) { public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); binding.addActivityResultListener(this); + smsReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Map result = new HashMap<>(); + result.put("state", "smsParsed"); + result.put("action_id", intentNullAwareString(intent, "action_id")); + result.put("response_message", intentNullAwareString(intent, "response_message")); + result.put("status", intentNullAwareString(intent, "status")); + result.put("status_meaning", intentNullAwareString(intent, "status_meaning")); + result.put("status_description", intentNullAwareString(intent, "status_description")); + result.put("uuid", intentNullAwareString(intent, "uuid")); + result.put("im_hni", intentNullAwareString(intent, "im_hni")); + result.put("environment", intent.getIntExtra("environment", 0)); + result.put("request_timestamp", intent.getIntExtra("request_timestamp", 0)); + result.put("response_timestamp", intent.getIntExtra("response_timestamp", 0)); + + result.put("matched_parser_id", intentNullAwareString(intent, "matched_parser_id")); + result.put("messagetype", intentNullAwareString(intent, "messagetype")); + result.put("message_sender", intentNullAwareString(intent, "message_sender")); + result.put("regex", intentNullAwareString(intent, "regex")); + + + transactionEventSink.success(result); + } + }; + LocalBroadcastManager.getInstance(activity.getBaseContext()).registerReceiver(smsReceiver, new IntentFilter(activity.getPackageName() + ".SMS_MISS")); } @@ -224,17 +222,11 @@ public void onDetachedFromActivityForConfigChanges() { activity = null; } - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - - eventSink.endOfStream(); - - - } @Override public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); + binding.addActivityResultListener(this); } @Override @@ -250,14 +242,14 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { String uuid = data.hasExtra("uuid") ? data.getStringExtra("uuid") : ""; Map result = new HashMap<>(); - result.put("state", "ussdSucceded"); + result.put("state", "ussdSucceeded"); result.put("uuid", uuid); if (data.hasExtra("session_messages")) { String[] sessionMessages = data.getStringArrayExtra("session_messages"); result.put("ussdSessionMessages", sessionMessages); } - eventSink.success(result); + transactionEventSink.success(result); return true; @@ -267,35 +259,25 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { if (data != null) { result.put("errorMessage", data.getStringExtra("error")); } - eventSink.success(result); - - return false; - } else { - Map result = new HashMap<>(); + transactionEventSink.success(result); - - result.put("state", "ussdFailed"); - if (data != null) { - result.put("errorMessage", data.getStringExtra("error")); - } - eventSink.success(result); return false; } - + //else { +// Map result = new HashMap<>(); +// +// +// result.put("state", "ussdFailed"); +// if (data != null) { +// result.put("errorMessage", data.getStringExtra("error")); +// } +// transactionEventSink.success(result); +// return false; + // } + + return false; } - @Override - public void onListen(Object arguments, EventChannel.EventSink events) { - if (eventSink == null) { - eventSink = events; - } - } - - @Override - public void onCancel(Object arguments) { - eventSink = null; - } - } \ No newline at end of file diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 03954a6..1c44ba2 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -30,8 +30,5 @@ - diff --git a/example/lib/main.dart b/example/lib/main.dart index 2c6aad8..bba26c7 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,10 +4,12 @@ import 'package:hover_ussd/hover_ussd.dart'; import 'package:hover_ussd/models/transaction_state.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({super.key}); + @override Widget build(BuildContext context) { return MaterialApp( @@ -16,112 +18,200 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: MyAppHomePage(), + home: const MyHomePage(), ); } } -class MyAppHomePage extends StatefulWidget { +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key}); + @override - _MyAppHomePageState createState() => _MyAppHomePageState(); + State createState() => _MyHomePageState(); } -class _MyAppHomePageState extends State { - final HoverUssd _hoverUssd = HoverUssd(); +class _MyHomePageState extends State { + late final HoverUssd _hoverUssd = HoverUssd(); late StreamSubscription _transactionListening; - - Future initHover(BuildContext context) async { - List? actions = await _hoverUssd.initialize( - branding: 'Hover Ussd Example', logo: "mipmap/ic_launcher"); - - if (actions == null) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Action download failed"))); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Action download successfully"))); - } - } - - Future getAllActions() async { - List? action = await _hoverUssd.getActions(); - - if (action == null) { - print("Action download failed"); - } else { - print(action); - } - } + late StreamSubscription _actionDownloadListening; + bool _hasPermissions = false; + bool? _isActionDownloaded; + String? _actionDownloadError; @override void initState() { - initHover(context); - getAllActions(); - + super.initState(); _transactionListening = _hoverUssd.getUssdTransactionState().listen((event) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(event.toMap().toString()))); + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(event.toMap().toString()))); + + _actionDownloadListening = _hoverUssd + .getDownloadActionState() + .listen((DonwloadActionState event) { + print(event.toMap()); + if (event is ActionDownloaded) { + setState(() { + _isActionDownloaded = event.isDownloaded; + }); + } + + if (event is ActionDownloadFailed) { + setState(() { + _actionDownloadError = event.error; + }); + } + }); }); - super.initState(); + + _checkPermissions(); + _initHover(); } @override void dispose() { _transactionListening.cancel(); + _actionDownloadListening.cancel(); super.dispose(); } + Future _checkPermissions() async { + bool hasPermissions = await _hoverUssd.hasAllPermissions(); + setState(() { + _hasPermissions = hasPermissions; + }); + } + + Future _initHover() async { + try { + await _hoverUssd.initialize( + apiKey: "15ccc2bd81801d8c5fbfd5847d3b4e77", + branding: "My Hover App", + logo: "ic_launcher", + notificationLogo: "ic_launcher", + ); + } catch (e) { + print(e); + } + } + + Future _getActions() async { + final actions = await _hoverUssd.getAllActions(); + for (var action in actions) { + print(action.toMap()); + } + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Hover Ussd Example'), ), - body: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - TextButton( - onPressed: () { - _hoverUssd.startTransaction( - actionId: "93c9da25", - extras: {}, - theme: "myHoverTheme", - header: "Hover Ussd Example", - showUserStepDescriptions: true); - }, - child: const Text("Start Transaction"), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () { + _hoverUssd.startTransaction( + actionId: "c6e45e62", + extras: {"price": "4000"}, + theme: "myHoverTheme", + header: "Hover Ussd Example", + showUserStepDescriptions: true, + ); + }, + child: const Text("Start Transaction"), + ), + ElevatedButton( + onPressed: _getActions, + child: const Text("Get Actions"), + ), + + //refresh actions + ElevatedButton( + onPressed: () { + _hoverUssd.refreshActions(); + }, + child: const Text("Refresh Actions"), + ), + Text( + _hasPermissions + ? "Permissions Granted" + : "Permissions Not Granted", + style: TextStyle( + color: _hasPermissions ? Colors.green : Colors.red, + fontWeight: FontWeight.bold, ), - StreamBuilder( - stream: _hoverUssd.getUssdTransactionState(), - builder: (BuildContext context, snapshot) { - if (snapshot.data is SmsParsed) { - return Text( - "Sms parsed : \n${(snapshot.data as SmsParsed).toMap()}"); - } + ), - if (snapshot.data is UssdSucceeded) { - return Text("Ussd Succeded : \n${(snapshot.data as UssdSucceeded).toMap()}"); - } - if (snapshot.data is UssdLoading) { - return const Text("loading..."); - } - if (snapshot.data is UssdFailed) { - return Text("Ussd Failed : \n${(snapshot.data as UssdFailed).toMap()}"); - } - if (snapshot.data is EmptyState) { - return const Text("Empty State"); + // stream of actiondownload status + + StreamBuilder( + stream: _hoverUssd.getDownloadActionState(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Text( + "Action Download Status: Unknown", + style: TextStyle( + color: Colors.grey, fontWeight: FontWeight.bold), + ); + } else { + final state = snapshot.data; + + String statusText; + Color textColor; + + if (state is ActionDownloaded) { + statusText = "Actions Downloaded"; + textColor = Colors.green; + } else if (state is ActionDownloadFailed) { + statusText = "Actions Not Downloaded"; + textColor = Colors.red; + } else if (state is ActionDownloading) { + statusText = "Actions Downloading"; + textColor = + Colors.blue; // Adjust color as per your preference + } else { + statusText = "Action Download Status: Unknown"; + textColor = Colors.grey; } - return const Text("No state"); - }, - ), - ], - ), - ], + return Text( + statusText, + style: TextStyle( + color: textColor, fontWeight: FontWeight.bold), + ); + } + }, + ), + StreamBuilder( + stream: _hoverUssd.getUssdTransactionState(), + builder: (BuildContext context, snapshot) { + if (snapshot.data is SmsParsed) { + return Text("Sms parsed : \n${snapshot.data!.toMap()}"); + } + + if (snapshot.data is UssdSucceeded) { + return Text("Ussd Succeded : \n${snapshot.data!.toMap()}"); + } + if (snapshot.data is UssdLoading) { + return const Text("loading..."); + } + if (snapshot.data is UssdFailed) { + return Text("Ussd Failed : \n${snapshot.data!.toMap()}"); + } + if (snapshot.data is EmptyState) { + return const Text("Empty State"); + } + + return const Text("No state"); + }, + ), + ], + ), ), ); } diff --git a/example/pubspec.lock b/example/pubspec.lock index f9a7818..6dc82ec 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" cupertino_icons: dependency: "direct main" description: @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" flutter: dependency: "direct main" description: flutter @@ -105,6 +105,30 @@ packages: description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: @@ -117,42 +141,42 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.11.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" platform: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: @@ -165,10 +189,10 @@ packages: dependency: transitive description: name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" url: "https://pub.dev" source: hosted - version: "4.2.4" + version: "5.0.2" sky_engine: dependency: transitive description: flutter @@ -186,18 +210,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -226,10 +250,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" vector_math: dependency: transitive description: @@ -242,26 +266,18 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f - url: "https://pub.dev" - source: hosted - version: "11.7.1" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "13.0.0" webdriver: dependency: transitive description: name: webdriver - sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" sdks: - dart: ">=3.1.3 <4.0.0" + dart: ">=3.2.0-0 <4.0.0" flutter: ">=3.3.0" diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart index 3a9177d..f089c51 100644 --- a/lib/hover_ussd.dart +++ b/lib/hover_ussd.dart @@ -1,4 +1,5 @@ export 'hover_ussd_plugin.dart'; export 'models/transaction.dart'; export 'models/transaction_state.dart'; -export 'models/hover_action.dart'; \ No newline at end of file +export 'models/hover_action.dart'; +export 'models/download_action_state.dart'; \ No newline at end of file diff --git a/lib/hover_ussd_plugin.dart b/lib/hover_ussd_plugin.dart index 5ad3a5e..fc8b5ac 100644 --- a/lib/hover_ussd_plugin.dart +++ b/lib/hover_ussd_plugin.dart @@ -1,37 +1,55 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import 'package:hover_ussd/models/hover_action.dart'; +import 'package:hover_ussd/hover_ussd.dart'; -import 'models/transaction.dart'; -import 'models/transaction_state.dart'; +import 'models/download_action_state.dart'; class HoverUssd { - static const MethodChannel _methodChannel = - MethodChannel('HoverUssdChannel'); - static const EventChannel _eventChannel = - EventChannel('TransactionEvent'); + static const MethodChannel _methodChannel = MethodChannel('HoverUssdChannel'); + static const EventChannel _transactionEventChannel = EventChannel('TransactionEvent'); + static const EventChannel _downloadEventChannel = EventChannel('ActionDownloadEvent'); Stream? _onTransactionStateChanged; /// Initialize HoverUssd with branding and logo information. - Future?> initialize( - {String? branding, String? logo}) async { - List? hoverActions = await _methodChannel.invokeMethod( - "Initialize", - {"branding": branding ?? "Flutter App", "logo": logo ?? "ic_launcher"}); - - return hoverActions; + /// + /// [apiKey] is the Hover API key. + /// [branding] is the name of the app. + /// [logo] is the id of the app's logo in ressource. + /// [notificationLogo] is the id of the app's logo in ressource. + /// + Future initialize({ + required String apiKey, + String? branding, + String? logo, + String? notificationLogo, + }) async { + await _methodChannel.invokeMethod("Initialize", { + "branding": branding ?? "Flutter App", + "logo": logo ?? "ic_launcher", + "notificationLogo": notificationLogo ?? "ic_launcher", + "apiKey": apiKey, + }); } /// Start a transaction with the provided parameters. + /// [actionId] is the id of the action to be performed. + /// [extras] is a map of extra parameters to be sent to the action. + /// [theme] is the theme of the transaction. + /// [header] is the header of the transaction. + /// [initialProcessingMessage] is the message to be displayed while the transaction is being processed. + /// [finalMsgDisplayTime] is the time to display the final message. + /// [showUserStepDescriptions] is a flag to show the user step descriptions. + /// Future startTransaction({ required String actionId, Map? extras, String? theme, String? header, String? initialProcessingMessage, - int finalMsgDisplayTime = 5000, bool? showUserStepDescriptions, + int finalMsgDisplayTime = 5000, + bool? showUserStepDescriptions, }) async { await _methodChannel.invokeMethod("HoverStartATransaction", { "actionId": actionId, @@ -54,26 +72,50 @@ class HoverUssd { } /// Retrieve a list of available actions. - Future> getActions() async { - List> result = await _methodChannel.invokeListMethod( - "getAllActions") as List>; - List actions = result.map((item) => HoverAction.fromMap(item)).toList(); - return actions; + Future> getAllActions() async { + try { + final result = await _methodChannel.invokeListMethod("getAllActions"); + if (result != null) { + List actions = + result.map((item) => HoverAction.fromMap(item.cast())).toList(); + return actions; + } else { + return []; // Or handle the null case according to your requirements. + } + } catch (e) { + print('Error retrieving actions: $e'); + return []; // Or handle the error case according to your requirements. + } + } + + //refresh actions + Future refreshActions() async { + await _methodChannel.invokeMethod("refreshActions"); } - /// Deprecated: Retrieve all transactions. - @Deprecated('As usehover v2') Future> getAllTransactions() async { - List> result = await _methodChannel - .invokeListMethod("getAllTransaction") as List>; - List transactions = result.map((item) => Transaction.fromMap(item)).toList(); - return transactions; + try { + final result = + await _methodChannel.invokeListMethod("getAllTransactions"); + if (result != null) { + final List> resultList = + List>.from(result); + List transactions = + resultList.map((item) => Transaction.fromMap(item)).toList(); + return transactions; + } else { + return []; // Or handle the null case according to your requirements. + } + } catch (e) { + print('Error retrieving transactions: $e'); + return []; // Or handle the error case according to your requirements. + } } /// Get the stream of transaction states. Stream getUssdTransactionState() { if (_onTransactionStateChanged == null) { - _onTransactionStateChanged ??= _eventChannel + _onTransactionStateChanged ??= _transactionEventChannel .receiveBroadcastStream() .map((dynamic event) => _parseTransactionState( event['state'], @@ -85,15 +127,42 @@ class HoverUssd { return _onTransactionStateChanged!; } + Stream getDownloadActionState() { + return _downloadEventChannel + .receiveBroadcastStream() + .map((dynamic event) => _parseDownloadActionState( + event['state'], + Map.from(event), + )) + .asBroadcastStream(); + } + + DonwloadActionState _parseDownloadActionState( + String state, + Map result, + ) { + switch (state) { + case "actionDownloaded": + return ActionDownloaded.fromMap(result); + case "actionDownloadFailed": + return ActionDownloadFailed.fromMap(result); + case "actionDownloading": + return ActionDownloading(); + default: + return EmptyDownloadState(); + } + } + /// Parse transaction state from received event. TransactionState _parseTransactionState( String state, Map result, ) { + print("State: $state"); switch (state) { case "smsParsed": return SmsParsed.fromMap(result); - case "ussdSucceded": + case "ussdSucceeded": return UssdSucceeded.fromMap(result); case "ussdFailed": return UssdFailed.fromMap(result); @@ -103,4 +172,4 @@ class HoverUssd { return EmptyState(); } } -} \ No newline at end of file +} diff --git a/lib/models/download_action_state.dart b/lib/models/download_action_state.dart new file mode 100644 index 0000000..ff101ad --- /dev/null +++ b/lib/models/download_action_state.dart @@ -0,0 +1,59 @@ +abstract class DonwloadActionState { + const DonwloadActionState(); + Map toMap(); +} + +class ActionDownloadFailed extends DonwloadActionState { + final String error; + + ActionDownloadFailed(this.error); + + @override + Map toMap() { + return { + "state": "actionDownloadFailed", + "error": error, + }; + } + + factory ActionDownloadFailed.fromMap(Map map) { + return ActionDownloadFailed(map['error']); + } +} + +class ActionDownloading extends DonwloadActionState { + @override + Map toMap() { + return { + "state": "actionDownloading", + }; + } +} + +class ActionDownloaded extends DonwloadActionState { + final bool isDownloaded; + + ActionDownloaded(this.isDownloaded); + + @override + Map toMap() { + return { + "state": "actionDownloaded", + "isDownloaded": isDownloaded, + }; + } + + factory ActionDownloaded.fromMap(Map map) { + return ActionDownloaded(map['isDownloaded']); + } + +} + +class EmptyDownloadState extends DonwloadActionState { + @override + Map toMap() { + return { + "state": "empty", + }; + } +} \ No newline at end of file diff --git a/lib/models/hover_action.dart b/lib/models/hover_action.dart index 3444887..049ddc9 100644 --- a/lib/models/hover_action.dart +++ b/lib/models/hover_action.dart @@ -1,48 +1,119 @@ class HoverAction { - String id; - String name; - String networkName; - String rootCode; - String transportType; - String country; - List steps; - List hnis; + int? id; + String? publicId; + String? name; + int? channelId; + String? networkName; + String? countryAlpha2; + String? rootCode; + String? transportType; + String? transactionType; + int? fromInstitutionId; + String? fromInstitutionName; + String? fromInstitutionLogo; + int? toInstitutionId; + String? toInstitutionName; + String? toInstitutionLogo; + String? toCountryAlpha2; + int? createdTimestamp; + int? updatedTimestamp; + int? bountyAmount; + bool? bountyIsOpen; + bool? isReady; + int? bonusPercent; + String? bonusMessage; + List? hniList; + List? customSteps; + List? tagsList; + Map? requiredParams; + Map? outputParams; HoverAction({ - required this.id, - required this.name, - required this.networkName, - required this.rootCode, - required this.transportType, - required this.country, - required this.steps, - required this.hnis, + this.id, + this.publicId, + this.name, + this.channelId, + this.networkName, + this.countryAlpha2, + this.rootCode, + this.transportType, + this.transactionType, + this.fromInstitutionId, + this.fromInstitutionName, + this.fromInstitutionLogo, + this.toInstitutionId, + this.toInstitutionName, + this.toInstitutionLogo, + this.toCountryAlpha2, + this.createdTimestamp, + this.updatedTimestamp, + this.bountyAmount, + this.bountyIsOpen, + this.isReady, + this.bonusPercent, + this.bonusMessage, + this.hniList, + this.customSteps, + this.tagsList, + this.requiredParams, + this.outputParams, }); + factory HoverAction.fromMap(Map json) { + return HoverAction( + id: json['id'], + publicId: json['public_id'], + name: json['name'], + channelId: json['channel_id'], + networkName: json['network_name'], + countryAlpha2: json['country_alpha2'], + rootCode: json['root_code'], + transportType: json['transport_type'], + transactionType: json['transaction_type'], + fromInstitutionId: json['from_institution_id'], + fromInstitutionName: json['from_institution_name'], + fromInstitutionLogo: json['from_institution_logo'], + toInstitutionId: json['to_institution_id'], + toInstitutionName: json['to_institution_name'], + toInstitutionLogo: json['to_institution_logo'], + toCountryAlpha2: json['to_country_alpha2'], + createdTimestamp: json['created_timestamp'], + updatedTimestamp: json['updated_timestamp'], + bountyAmount: json['bounty_amount'], + bountyIsOpen: json['bounty_is_open'], + isReady: json['is_ready'], + bonusPercent: json['bonus_percent'], + bonusMessage: json['bonus_message'], + requiredParams: json['required_params'], + outputParams: json['output_params'], + ); + } + Map toMap() { return { 'id': id, + 'public_id': publicId, 'name': name, - 'networkName': networkName, - 'rootCode': rootCode, - 'transportType': transportType, - 'country': country, - 'steps': steps, - 'hnis': hnis, + 'channel_id': channelId, + 'network_name': networkName, + 'country_alpha2': countryAlpha2, + 'root_code': rootCode, + 'transport_type': transportType, + 'transaction_type': transactionType, + 'from_institution_id': fromInstitutionId, + 'from_institution_name': fromInstitutionName, + 'from_institution_logo': fromInstitutionLogo, + 'to_institution_id': toInstitutionId, + 'to_institution_name': toInstitutionName, + 'to_institution_logo': toInstitutionLogo, + 'to_country_alpha2': toCountryAlpha2, + 'created_timestamp': createdTimestamp, + 'updated_timestamp': updatedTimestamp, + 'bounty_amount': bountyAmount, + 'bounty_is_open': bountyIsOpen, + 'is_ready': isReady, + 'bonus_percent': bonusPercent, + 'bonus_message': bonusMessage, }; } - - //from map - factory HoverAction.fromMap(Map json) { - return HoverAction( - id: json['id'] as String, - name: json['name'] as String, - networkName: json['networkName'] as String, - rootCode: json['rootCode'] as String, - transportType: json['transportType'] as String, - country: json['country'] as String, - steps: json['steps'].cast(), - hnis: json['hnis'].cast(), - ); - } -} \ No newline at end of file +} diff --git a/pubspec.lock b/pubspec.lock index 166d22c..3bdcb83 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" fake_async: dependency: transitive description: @@ -67,6 +67,30 @@ packages: description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: @@ -79,34 +103,34 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.11.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" plugin_platform_interface: dependency: "direct main" description: @@ -132,18 +156,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -164,10 +188,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" vector_math: dependency: transitive description: @@ -176,14 +200,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "13.0.0" sdks: - dart: ">=3.1.3 <4.0.0" + dart: ">=3.2.0-0 <4.0.0" flutter: ">=3.3.0" From fda38c86d562598b03166b910748d967f7c1418b Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Sat, 9 Mar 2024 05:14:06 +0200 Subject: [PATCH 29/36] final fix candidate 6 --- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 10 ++-- example/lib/main.dart | 58 ++++++++++++++++++- lib/hover_ussd_plugin.dart | 8 +-- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index dcf4c83..8f57cbd 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -51,26 +51,28 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin EventChannel transactionEventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "TransactionEvent"); EventChannel actionDownloadEventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "ActionDownloadEvent"); - transactionEventChannel.setStreamHandler(new EventChannel.StreamHandler() { + actionDownloadEventChannel.setStreamHandler(new EventChannel.StreamHandler() { @Override public void onListen(Object arguments, EventChannel.EventSink events) { - transactionEventSink = events; + actionDownloadEventSink = events; } @Override public void onCancel(Object arguments) { } }); - actionDownloadEventChannel.setStreamHandler(new EventChannel.StreamHandler() { + + transactionEventChannel.setStreamHandler(new EventChannel.StreamHandler() { @Override public void onListen(Object arguments, EventChannel.EventSink events) { - actionDownloadEventSink = events; + transactionEventSink = events; } @Override public void onCancel(Object arguments) { } }); + channel.setMethodCallHandler(this); } diff --git a/example/lib/main.dart b/example/lib/main.dart index bba26c7..b3de297 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -65,7 +65,9 @@ class _MyHomePageState extends State { }); _checkPermissions(); - _initHover(); + Future.delayed(Duration.zero, () { + _initHover(); + }); } @override @@ -95,11 +97,19 @@ class _MyHomePageState extends State { } } - Future _getActions() async { + Future _getAndGoToActions() async { final actions = await _hoverUssd.getAllActions(); for (var action in actions) { print(action.toMap()); } + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => HoverActionListPage( + hoverActions: actions, + ), + ), + ); } @override @@ -126,7 +136,7 @@ class _MyHomePageState extends State { child: const Text("Start Transaction"), ), ElevatedButton( - onPressed: _getActions, + onPressed: _getAndGoToActions, child: const Text("Get Actions"), ), @@ -216,3 +226,45 @@ class _MyHomePageState extends State { ); } } + +class HoverActionListPage extends StatefulWidget { + final List hoverActions; + + const HoverActionListPage({Key? key, required this.hoverActions}) + : super(key: key); + + @override + _HoverActionListPageState createState() => _HoverActionListPageState(); +} + +class _HoverActionListPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Hover Action List'), + ), + body: ListView.builder( + itemCount: widget.hoverActions.length, + itemBuilder: (BuildContext context, int index) { + final hoverAction = widget.hoverActions[index]; + return ListTile( + title: Text(hoverAction.name ?? ''), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Network: ${hoverAction.networkName ?? ''}'), + Text('Country: ${hoverAction.countryAlpha2 ?? ''}'), + Text('Transaction Type: ${hoverAction.transactionType ?? ''}'), + // Add more details as needed + ], + ), + onTap: () { + // Handle onTap if needed + }, + ); + }, + ), + ); + } +} diff --git a/lib/hover_ussd_plugin.dart b/lib/hover_ussd_plugin.dart index fc8b5ac..dbd1128 100644 --- a/lib/hover_ussd_plugin.dart +++ b/lib/hover_ussd_plugin.dart @@ -80,11 +80,11 @@ class HoverUssd { result.map((item) => HoverAction.fromMap(item.cast())).toList(); return actions; } else { - return []; // Or handle the null case according to your requirements. + return []; } } catch (e) { print('Error retrieving actions: $e'); - return []; // Or handle the error case according to your requirements. + return []; } } @@ -104,11 +104,11 @@ class HoverUssd { resultList.map((item) => Transaction.fromMap(item)).toList(); return transactions; } else { - return []; // Or handle the null case according to your requirements. + return []; } } catch (e) { print('Error retrieving transactions: $e'); - return []; // Or handle the error case according to your requirements. + return []; } } From de19e47e53be82ef75cb85e7b0f62f605c3d66a2 Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Sat, 9 Mar 2024 13:27:47 +0200 Subject: [PATCH 30/36] fix customisation, minor improvement --- .../lucdotdev/hover_ussd/HoverUssdApi.java | 47 +-- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 25 +- .../app/src/main/res/values/styles.xml | 370 +++++++++++++++++- example/lib/main.dart | 84 ++-- 4 files changed, 415 insertions(+), 111 deletions(-) diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index b5ffcaf..3ecd5df 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -4,7 +4,6 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.util.Log; import com.hover.sdk.actions.HoverAction; import com.hover.sdk.api.Hover; @@ -24,37 +23,27 @@ public class HoverUssdApi { private final Activity activity; - private final EventChannel.EventSink eventSink; - private final Context context; + public HoverUssdApi(Activity activity, EventChannel.EventSink eventSink) { this.activity = activity; - this.eventSink = eventSink; this.context = activity.getApplicationContext(); } public void initialize(String apiKey, String branding, String logo, String notificationLogo, Hover.DownloadListener downloadListener) { Hover.initialize(context, apiKey, true, downloadListener); - if (branding != null && logo != null && notificationLogo != null) { - int logoResourceId = getResourceId(context, logo); - int notificationLogoResourceId = getResourceId(context, notificationLogo); - if (logoResourceId == 0) { - Log.e("HOVER_USSD_PLUGIN", ":LOGO NOT FOUND"); - } else { - Hover.setBranding(branding, logoResourceId, notificationLogoResourceId, context); - } - } + + int logoResourceId = getResourceId(logo == null ? "ic_launcher" : logo); + int notificationLogoResourceId = getResourceId(notificationLogo == null ? "ic_launcher" : notificationLogo); + Hover.setBranding(branding == null ? "Hover Ussd Plugin" : branding, logoResourceId, notificationLogoResourceId, context); + } public boolean hasAllPerms() { return Hover.hasAllPerms(activity.getApplicationContext()); } - - // get all actions from hover and convert to an list of array map - - public ArrayList> getAllActions() { List actions = HoverRoomDatabase.getInstance(context).actionDao().getAll(); ArrayList> mapActions = new ArrayList<>(); @@ -67,9 +56,6 @@ public ArrayList> getAllActions() { return mapActions; } - public void updateActionConfigs(Hover.DownloadListener actionDownloadListener) { - Hover.updateActionConfigs(actionDownloadListener, context); - } public void refreshActions(Hover.DownloadListener actionDownloadListener) { Hover.updateActionConfigs(actionDownloadListener, context); @@ -91,17 +77,14 @@ public ArrayList> getAllTransaction() { } - private int getResourceId(Context context, String resourceName) { + private int getResourceId(String resourceName) { try { - Class res = R.drawable.class; - Field field = res.getField(resourceName); - return field.getInt(null); + return context.getResources().getIdentifier(resourceName, "mipmap", activity.getPackageName()); } catch (Exception e) { e.printStackTrace(); - return 0; // Return 0 if resource ID is not found + return 0; } } - public void sendUssd(String action_id, HashMap extra, String theme, @@ -145,18 +128,10 @@ public void sendUssd(String action_id, builder.finalMsgDisplayTime(finalMsgDisplayTime); } - try { - Intent buildIntent = builder.buildIntent(); - activity.startActivityForResult(buildIntent, 0); - } catch (Exception e) { - Map result = new HashMap<>(); + Intent buildIntent = builder.buildIntent(); + activity.startActivityForResult(buildIntent,4000 ); - result.put("state", "ussdFailed"); - result.put("errorMessage", e); - eventSink.success(result); - - } } } diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index 8f57cbd..f63a3b8 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -98,7 +98,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result public void onError(String s) { Map result = new HashMap<>(); result.put("state", "actionDownloadFailed"); - result.put("error", "error"); + result.put("error", s); actionDownloadEventSink.success(result); } @@ -127,7 +127,7 @@ public void onSuccess(ArrayList arrayList) { public void onError(String s) { Map result = new HashMap<>(); result.put("state", "actionDownloadFailed"); - result.put("error", "error"); + result.put("error", s); actionDownloadEventSink.success(result); } @@ -240,7 +240,8 @@ public void onDetachedFromActivity() { @Override public boolean onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == 0 && resultCode == Activity.RESULT_OK) { + + if (requestCode == 4000 && resultCode == Activity.RESULT_OK) { String uuid = data.hasExtra("uuid") ? data.getStringExtra("uuid") : ""; Map result = new HashMap<>(); @@ -255,29 +256,17 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { return true; - } else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED) { + } else if (requestCode == 4000 && data.hasExtra("error")) { Map result = new HashMap<>(); result.put("state", "ussdFailed"); - if (data != null) { - result.put("errorMessage", data.getStringExtra("error")); - } + result.put("errorMessage", data.getStringExtra("error")); transactionEventSink.success(result); return false; } - //else { -// Map result = new HashMap<>(); -// -// -// result.put("state", "ussdFailed"); -// if (data != null) { -// result.put("errorMessage", data.getStringExtra("error")); -// } -// transactionEventSink.success(result); -// return false; - // } return false; + } diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index cb1ef88..d1001e6 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,5 +1,7 @@ - + + + + + + + + #FF4C00 + #F1F1F4 + #292E35 + #1E232A + #8AFF4C00 + #CC3E02 + #FFFFFF + #F1F1F4 + #54575D + #FFCA00 + #374B5F + #28aae1 + #5f8cb4 + #EB7D23 + #CD2328 + #F5C346 + #F0F0F0 + #0A9A8D + #4285F4 + #263238 + #202020 + #737373 + #0A9A8D + #000000 + #5f8cb4 + #D0021B + #FF999999 + #11122C + #4A4A4A + #80000000 + #80FFFFFF + #00FFFFFF + #C8616161 + #DEDEDE + #808080 + #BDBDBD + #FFFFFF + #34383F + #23FFFFFF + 13dp + 5dp + 24dp + 128dp + 55dp + 21dp + 150dp + 8dp + 16dp + 16sp + 4dp + 32dp + 19sp + This is used to read and interact with USSD menus to provide a simplified and more accessible experience. + + Connect with Mobile Money and Telecom services on your + behalf + buy airtime + check balance + Fetch accounts + https://usehover.com + Open Settings + To prevent Accessibility from automatically turning off, go + to Settings and disable battery optimization for this app. + + Find and Tap %1$s then turn it ON + Turn on Accessibility in the next screen to run USSD sessions]]> + Step 1 + Touch this button + Step 2 + Switch ON + Find \"%1$s\" in the list and turn it ON + Accessibility + ERROR CODE 5000: Accessibility settings could not be found + Services + WARNING: Accessibility got timeout. Shutting down + e3b51457136ad623213ade89a42a288f + Accessibility + All + Removed screen blocker + Pressed cancel + Matched custom stop event, ending at step %1$s + Showing confirm transaction details + Initializing USSD services + Denied + Exception + Execution failed. Action: %1$s, AccessNode: %2$s + Transacting failed + Failure + False Start + Finished + Finished due to SMS match + Granted + Intializing + Interrupt + Keystore + Failed to generate keystore key for PIN + Reacting to Dual SIM + Matched + Mocking + Showing go online to check Hover API key + Showing enter a correct SIM + Parser + Permission + Could not decrypt PIN + Could not encrypt PIN + Pressed submit PIN + Could not find PIN + Showing PIN entry screen + Could not generate PrivateKeyEntry after 4 attempts + Reacting + Requesting Accessiblity + Requesting Phone and SMS + Requesting Overlay + Hover Session + Added screen blocker + Step not found, skipping + Starting + STK not found + Transacting succeeded + App Name + Back + back icon + backspace + https://www.usehover.com/sdk/ + We need this permission to read your SIM card and SMS responses to USSD requests + battery icon + Cancel + channel_actions + Choose SIM + Close + Transaction complete + Details fetched + Confirm + Please turn on mobile data or Wi-Fi to complete this + request. + Continue + Debug environment active. You will see USSD menus flash on screen. See Android Studio logcat for messages. + Hover Menu Service + Communicating with USSD service… + 8 + Enable Accessibility + %1$s + Enter PIN + invalid-mmi-code + ERROR CODE 7001: Unknown error, please try again. It may an issue with your device, network, or SIM card(s). If the problem persists please contact Hover. + Please check your PIN and try again. + 5 + 4 + hand icon + Internet Required + ERROR CODE 2001: There was a problem processing the actions\' configuration, please contact Hover for help. + ERROR CODE 1004: Network problem while attempting actions download. Trying again. + Successfully saved %1$d action(s) + Successfully imported %1$d action(s) + ERROR CODE 1005: This version of Android is not supported + ERROR CODE 2003: Internet problem, trying again. + API key is valid + Confirmed request. + WARNING: Failed to load cursor + Downloading actions + ERROR CODE 1002: Could not validate API Key, see web dashboard for details. Is it defined as metadata inside your application tag in your Manifest. Does it match the key and package name defined in your web dashboard, and is your billing up to date? + Accessibility service threw exception. + WARNING: Failed parser match. + Importing cached actions from json file + Initiating request: %1$s %2$s + ERROR CODE 1013: Environment must be TEST_ENV, DEBUG_ENV, or PROD_ENV + ERROR CODE 6001: Received invalid Intent + ERROR CODE 1011: You have selected a sim with HNI %1$s, but the action\'s only valid choices are %2$s + ERROR 5010: Problem with device keystore. Please contact Hover with your device model if the problem continues. + ERROR CODE 1008: Action not found. If you have updated your actions in the Hover dashboard you must ensure that they are updated in your client which happens when `%1$s.initialize()` is called. + ERROR CODE 1006: You must specify an Action ID + ERROR CODE 1012: Missing parameter. You must specify: %1$s + ERROR CODE 1001: Could not find API Key. Is it defined as metadata inside your application tag in your Manifest? + ERROR CODE 1000: Could not get package name. Have you defined an applicationID in your build.gradle? + ERROR CODE 1003: Please call %1$s.initialize() before using this function + Parser %1$d matched message: %2$s. + ERROR 5012: Could not decrypt PIN. Please contact Hover with your device model if the problem continues. + ERROR 5011: Could not encrypt PIN. Please contact Hover with your device model if the problem continues. + Problem with PIN + Parser matched an SMS, ending session. + Finished carrying out action, cleaning up. + Received response from operator, carrying out action. + Successfully ran action. + Request valid, confirming any variables or PIN with user. + SMS HIT. + WARNING: SMS miss. Check that sender match is case sensitive and regex matches whole message. + Received SMS from %1$s: %2$s + SMS sender matched a parser. + Timeout triggered + ERROR 3001: Timeout reached, please check your USSD network connection and your action configurations. + Manual environment active. This session will be recorded. + sms_logs + network icon + next icon + 9 + Off + OK + 1 + Allow display over other apps + Transacting + Show progress updates by turning switch ON in the next screen.]]> + Next, tap the switch ON + ACCESSIBILITY + DISPLAY_OVERLAY + READ_PHONE_STATE + READ_SMS + permission icon + Permissions needed + We need this permission to read your SIM card and dial USSD numbers on your behalf + phone border icon + PIN character + Please wait + custom_actions + Loading, one moment… + Processing response SMS, one moment… + response icon + ERROR 6003: You must specify a list of actions + ERROR 1007: No actions found. Do you have some in your account, and have you called %1$s.initialize()? + ERROR 6005: Bad package name format. Expecting format: com.example.ActivityName or something similar + ERROR 6004: Invalid request: When you specify multiple action Ids they must be for different networks/SIM cards + ERROR CODE 1010: Key value pair was not valid + ERROR CODE 4002: User has not granted %1$s permission(s) + ERROR CODE 5003: Could not use USSD API + ERROR CODE 5001: Could not find Phone Dialer + ERROR CODE 6002: Please ensure you have READ_PHONE_STATE permission + ERROR CODE 5002: Could not find SIM Toolkit for correct SIM + ERROR CODE 5013: Could not use PIN. Please contact Hover with your device model if the problem continues. + ERROR CODE 4003: User has no SIM cards that work with the specified actions + ERROR CODE 4001: Request canceled by user + Private & Secure + https://19a2e15db8bf433fbab4458997b26eee@o44209.ingest.sentry.io/95446 + https://%1$s@sentry.io/95446?stacktrace.app.packages=com.hover.sdk&release=%2$s + USSD Session + Settings + 7 + SIM + 6 + parsing-for-variables + …still waiting… + forced-stopped-before-end + Test environment active. No actual transactions will take place. See Android Studio logcat for messages. + 3 + toggle icon + validate-token + transactions/batch + 2 + unknown + Communication error, please try again. + %1$s%2$s + Use Accessibility Service + By continuing, you allow this app to have the permission(s) above in order to serve you a fully functional app experience + Show Drawing Overlay + %1$s would like to: + Read SIM and make phone calls + Read SMS messages + wifi icon + likely-wrong-pin-detection + SIM not present + Please insert a %1$s SIM. + 0 + buy goods + money received + move money + To %1$s + On network + Someone else + pay bill + Secure with %1$s + %1$s never saves your PIN. We never see your PIN. Your PIN never leaves your device. When you put your PIN in the app, we encrypt the PIN on your device with Android Key Store, which is trusted by thousands of banks all over the world, and delete it as soon as your bank or mobile money provider has it. + Visit our FAQ page + To learn more about security in %1$s: + Myself + send money + https://www.stax.me/faq + use USSD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/lib/main.dart b/example/lib/main.dart index b3de297..47f744e 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -35,39 +35,19 @@ class _MyHomePageState extends State { late StreamSubscription _transactionListening; late StreamSubscription _actionDownloadListening; bool _hasPermissions = false; - bool? _isActionDownloaded; - String? _actionDownloadError; @override void initState() { - super.initState(); _transactionListening = _hoverUssd.getUssdTransactionState().listen((event) { ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text(event.toMap().toString()))); - - _actionDownloadListening = _hoverUssd - .getDownloadActionState() - .listen((DonwloadActionState event) { - print(event.toMap()); - if (event is ActionDownloaded) { - setState(() { - _isActionDownloaded = event.isDownloaded; - }); - } - - if (event is ActionDownloadFailed) { - setState(() { - _actionDownloadError = event.error; - }); - } - }); }); _checkPermissions(); - Future.delayed(Duration.zero, () { - _initHover(); - }); + _initHover(); + + super.initState(); } @override @@ -128,7 +108,7 @@ class _MyHomePageState extends State { _hoverUssd.startTransaction( actionId: "c6e45e62", extras: {"price": "4000"}, - theme: "myHoverTheme", + theme: "HoverTheme", header: "Hover Ussd Example", showUserStepDescriptions: true, ); @@ -162,39 +142,31 @@ class _MyHomePageState extends State { StreamBuilder( stream: _hoverUssd.getDownloadActionState(), builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Text( - "Action Download Status: Unknown", - style: TextStyle( - color: Colors.grey, fontWeight: FontWeight.bold), - ); + final state = snapshot.data; + + String statusText; + Color textColor; + + if (state is ActionDownloaded) { + statusText = "Actions Downloaded"; + textColor = Colors.green; + } else if (state is ActionDownloadFailed) { + statusText = "Actions Not Downloaded"; + textColor = Colors.red; + } else if (state is ActionDownloading) { + statusText = "Actions Downloading"; + textColor = + Colors.blue; // Adjust color as per your preference } else { - final state = snapshot.data; - - String statusText; - Color textColor; - - if (state is ActionDownloaded) { - statusText = "Actions Downloaded"; - textColor = Colors.green; - } else if (state is ActionDownloadFailed) { - statusText = "Actions Not Downloaded"; - textColor = Colors.red; - } else if (state is ActionDownloading) { - statusText = "Actions Downloading"; - textColor = - Colors.blue; // Adjust color as per your preference - } else { - statusText = "Action Download Status: Unknown"; - textColor = Colors.grey; - } - - return Text( - statusText, - style: TextStyle( - color: textColor, fontWeight: FontWeight.bold), - ); + statusText = "Action Download Status: Unknown"; + textColor = Colors.grey; } + + return Text( + statusText, + style: + TextStyle(color: textColor, fontWeight: FontWeight.bold), + ); }, ), StreamBuilder( @@ -234,7 +206,7 @@ class HoverActionListPage extends StatefulWidget { : super(key: key); @override - _HoverActionListPageState createState() => _HoverActionListPageState(); + State createState() => _HoverActionListPageState(); } class _HoverActionListPageState extends State { From 6c48425471ec9ebfa8e260a29d5105947dab254f Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Sat, 9 Mar 2024 13:30:13 +0200 Subject: [PATCH 31/36] fix activity result --- .../src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index f63a3b8..509a288 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -256,7 +256,7 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { return true; - } else if (requestCode == 4000 && data.hasExtra("error")) { + } else if (requestCode == 4000 && resultCode== Activity.RESULT_CANCELED && data != null) { Map result = new HashMap<>(); result.put("state", "ussdFailed"); result.put("errorMessage", data.getStringExtra("error")); From 99e50f99030080279715ad668004a742ce783a86 Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Tue, 12 Mar 2024 23:43:26 +0200 Subject: [PATCH 32/36] fix build error, add accessibility listen and overlay listen --- android/build.gradle | 11 ---- .../lucdotdev/hover_ussd/HoverUssdApi.java | 25 ++++++-- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 60 +++++++++++++++---- example/lib/main.dart | 44 ++++++++++++-- lib/hover_ussd_plugin.dart | 11 ++++ 5 files changed, 120 insertions(+), 31 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index c48c773..03ed6c4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -42,20 +42,9 @@ android { } dependencies { - compileOnly files('tempsLibs/flutter.jar') testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.0.0' implementation 'com.hover:android-sdk:2.0.0-beta04' } - - testOptions { - unitTests.all { - testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} - showStandardStreams = true - } - } - } } diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index 3ecd5df..1fd20b4 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -2,8 +2,12 @@ import android.app.Activity; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.hover.sdk.actions.HoverAction; import com.hover.sdk.api.Hover; @@ -26,7 +30,7 @@ public class HoverUssdApi { private final Context context; - public HoverUssdApi(Activity activity, EventChannel.EventSink eventSink) { + public HoverUssdApi(Activity activity) { this.activity = activity; this.context = activity.getApplicationContext(); } @@ -41,9 +45,18 @@ public void initialize(String apiKey, String branding, String logo, String notif } public boolean hasAllPerms() { - return Hover.hasAllPerms(activity.getApplicationContext()); + return Hover.hasAllPerms(context); + } + + public boolean isAccessibilityEnabled() { + return Hover.isAccessibilityEnabled(context); + } + + public boolean isOverlayEnabled() { + return Hover.isOverlayEnabled(context); } + public ArrayList> getAllActions() { List actions = HoverRoomDatabase.getInstance(context).actionDao().getAll(); ArrayList> mapActions = new ArrayList<>(); @@ -62,6 +75,7 @@ public void refreshActions(Hover.DownloadListener actionDownloadListener) { } + public ArrayList> getAllTransaction() { List transactions = HoverRoomDatabase.getInstance(context).transactionDao().getAll(); @@ -91,7 +105,8 @@ public void sendUssd(String action_id, String header, String initialProcessingMessage, boolean showUserStepDescriptions, - int finalMsgDisplayTime + int finalMsgDisplayTime, + BroadcastReceiver transactionStateReceiver ) { @@ -130,7 +145,9 @@ public void sendUssd(String action_id, Intent buildIntent = builder.buildIntent(); - activity.startActivityForResult(buildIntent,4000 ); + + + activity.startActivityForResult(buildIntent,0 ); } diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index 509a288..10516b7 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -18,6 +18,7 @@ import java.util.Map; import java.util.Objects; +import io.flutter.Log; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; @@ -31,7 +32,7 @@ */ -public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener{ +public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener { private Activity activity; @@ -40,9 +41,12 @@ public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChan private EventChannel.EventSink transactionEventSink; private EventChannel.EventSink actionDownloadEventSink; private BroadcastReceiver smsReceiver; + private BroadcastReceiver transactionStateReceiver; private HoverUssdApi hoverUssdApi; + private static final String TAG = "HOVER_USSD_PLUGIN"; + @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { @@ -55,13 +59,18 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin @Override public void onListen(Object arguments, EventChannel.EventSink events) { actionDownloadEventSink = events; + + Map result = new HashMap<>(); + result.put("state", "listening"); + + actionDownloadEventSink.success(result); } @Override public void onCancel(Object arguments) { } }); - + transactionEventChannel.setStreamHandler(new EventChannel.StreamHandler() { @Override public void onListen(Object arguments, EventChannel.EventSink events) { @@ -73,6 +82,7 @@ public void onCancel(Object arguments) { } }); + channel.setMethodCallHandler(this); } @@ -90,7 +100,7 @@ private String intentNullAwareString(Intent intent, String name) { @Override public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - hoverUssdApi = new HoverUssdApi(activity, transactionEventSink); + hoverUssdApi = new HoverUssdApi(activity); switch (call.method) { case "Initialize": hoverUssdApi.initialize(call.argument("apiKey"), call.argument("branding"), call.argument("logo"), call.argument("notificationLogo"), new Hover.DownloadListener() { @@ -110,11 +120,17 @@ public void onSuccess(ArrayList arrayList) { actionDownloadEventSink.success(result); } }); - + break; case "HasAllPermissions": result.success(hoverUssdApi.hasAllPerms()); break; + case "IsAccessibilityEnabled": + result.success(hoverUssdApi.isAccessibilityEnabled()); + break; + case "IsOverlayEnabled": + result.success(hoverUssdApi.isOverlayEnabled()); + break; case "getAllActions": result.success(hoverUssdApi.getAllActions()); break; @@ -146,6 +162,18 @@ public void onSuccess(ArrayList arrayList) { resultJson.put("state", "ussdLoading"); transactionEventSink.success(resultJson); + transactionStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent i) { + + transactionEventSink.success(i); + Log.i(TAG, "Received pending transaction created broadcast"); + Log.i(TAG, "uuid: " + i.getStringExtra("uuid")); + + + } + }; + try { hoverUssdApi.sendUssd( (String) call.argument("actionId"), @@ -162,7 +190,9 @@ public void onSuccess(ArrayList arrayList) { (String) call.argument("initialProcessingMessage") : "", false, - call.hasArgument("finalMsgDisplayTime") ? (int) call.argument("finalMsgDisplayTime") : 5000); + (int) call.argument("finalMsgDisplayTime"), + transactionStateReceiver + ); } catch (Exception e) { Map resultError = new HashMap<>(); @@ -217,6 +247,7 @@ public void onReceive(Context context, Intent intent) { }; LocalBroadcastManager.getInstance(activity.getBaseContext()).registerReceiver(smsReceiver, new IntentFilter(activity.getPackageName() + ".SMS_MISS")); + } @Override @@ -229,19 +260,27 @@ public void onDetachedFromActivityForConfigChanges() { public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); binding.addActivityResultListener(this); + } @Override public void onDetachedFromActivity() { LocalBroadcastManager.getInstance(activity.getBaseContext()).unregisterReceiver(smsReceiver); + + if (transactionStateReceiver != null) { + LocalBroadcastManager.getInstance(activity.getApplication()).unregisterReceiver(transactionStateReceiver); + } + activity = null; + } @Override public boolean onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == 4000 && resultCode == Activity.RESULT_OK) { + + if (requestCode == 0 && data != null && resultCode == Activity.RESULT_OK) { String uuid = data.hasExtra("uuid") ? data.getStringExtra("uuid") : ""; Map result = new HashMap<>(); @@ -256,19 +295,16 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { return true; - } else if (requestCode == 4000 && resultCode== Activity.RESULT_CANCELED && data != null) { + } else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED && data != null) { Map result = new HashMap<>(); result.put("state", "ussdFailed"); result.put("errorMessage", data.getStringExtra("error")); transactionEventSink.success(result); - - return false; + return true; } - return false; + return true; } - - } \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 47f744e..a150154 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -35,6 +35,8 @@ class _MyHomePageState extends State { late StreamSubscription _transactionListening; late StreamSubscription _actionDownloadListening; bool _hasPermissions = false; + bool isOverlayEnabled = false; + bool isAccessiblityEnabled = false; @override void initState() { @@ -44,9 +46,18 @@ class _MyHomePageState extends State { .showSnackBar(SnackBar(content: Text(event.toMap().toString()))); }); + _actionDownloadListening = + _hoverUssd.getDownloadActionState().listen((event) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(event.toString()))); + }); + + _actionDownloadListening.onData((event) { + _initHover(); + }); _checkPermissions(); - _initHover(); - + _isAccessiblityEnabled(); + _isOverlayEnabled(); super.initState(); } @@ -63,6 +74,15 @@ class _MyHomePageState extends State { _hasPermissions = hasPermissions; }); } + Future _isAccessiblityEnabled() async { + bool isAccessiblityEnabled = await _hoverUssd.isAccessiblityEnabled(); + print(isAccessiblityEnabled); + } + + Future _isOverlayEnabled() async { + bool isOverlayEnabled = await _hoverUssd.isOverlayEnabled(); + print(isOverlayEnabled); + } Future _initHover() async { try { @@ -136,8 +156,24 @@ class _MyHomePageState extends State { fontWeight: FontWeight.bold, ), ), - - // stream of actiondownload status + Text( + isAccessiblityEnabled + ? "Accessibility Granted" + : "Accessibility Not Granted", + style: TextStyle( + color: isAccessiblityEnabled ? Colors.green : Colors.red, + fontWeight: FontWeight.bold, + ), + ), + Text( + isOverlayEnabled + ? "Overlay Granted" + : "Overlay Not Granted", + style: TextStyle( + color: isOverlayEnabled ? Colors.green : Colors.red, + fontWeight: FontWeight.bold, + ), + ), StreamBuilder( stream: _hoverUssd.getDownloadActionState(), diff --git a/lib/hover_ussd_plugin.dart b/lib/hover_ussd_plugin.dart index dbd1128..d98b387 100644 --- a/lib/hover_ussd_plugin.dart +++ b/lib/hover_ussd_plugin.dart @@ -66,6 +66,17 @@ class HoverUssd { return await _methodChannel.invokeMethod("HasAllPermissions"); } + // this check if app accessibility permission is granted + Future isAccessiblityEnabled() async { + return await _methodChannel.invokeMethod("IsAccessiblityEnabled"); + } + + // check if app has overlay permission + Future isOverlayEnabled() async { + return await _methodChannel.invokeMethod("IsOverlayEnabled"); + } + + /// Check if the app has downloaded actions. Future hasActionsDownloaded() async { return await _methodChannel.invokeMethod("HasActionsDownloaded"); From 2b45331c7216739642902ed33080bad48a8b144f Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Thu, 14 Mar 2024 23:44:42 +0200 Subject: [PATCH 33/36] candidate 7 --- CHANGELOG.md | 3 + README.md | 404 ++++++++++++++---- .../lucdotdev/hover_ussd/HoverUssdApi.java | 38 +- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 324 +++++++------- example/lib/main.dart | 47 +- lib/hover_ussd_plugin.dart | 4 +- 6 files changed, 493 insertions(+), 327 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5364c8..03078de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 2.1.0 +* rethink entire plugin + ## 2.0.0 * revert to hover standart * change sendUssd() to startTransaction() diff --git a/README.md b/README.md index 0d5698c..fe83681 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ [![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) + © image by Francis Mwakitumbula @@ -15,90 +16,106 @@ A flutter plugin implemanting usehover.com ussd gateway sdk using Android Intent ## Getting Started -* Adding The hover api key refert to documentation at [docs.usehover.com](https://docs.usehover.com/) -```xml - -``` -## Basic Usage - -```dart -void main() { - WidgetsFlutterBinding.ensureInitialized(); - HoverUssd.initialize( - branding: 'Hover Ussd Example', logo: "mipmap/ic_launcher"); - runApp(MyApp()); -} +## Hover USSD Plugin -class MyApp extends StatelessWidget { - final HoverUssd _hoverUssd = HoverUssd(); +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. - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Hover Ussd Example'), - ), - body: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - TextButton( - onPressed: () { - _hoverUssd.startTransaction( - actionId: "c6e45e62", - extras: {"price": "4000"}, - theme: "myHoverTheme", - header: "Hover Ussd Example", - showUserStepDescriptions: true); - }, - child: Text("Start Transaction"), - ), - StreamBuilder( - stream: _hoverUssd.getUssdTransactionState, - builder: (BuildContext context, snapshot) { - if (snapshot.data is SmsParsed) { - return Text( - "Sms parsed : \n" + snapshot.data!.toMap().toString()); - } +## Usage - if (snapshot.data is UssdSucceded) { - return Text( - "Ussd Succeded : \n" + snapshot.data!.toMap().toString()); - } - if (snapshot.data is UssdLoading) { - return Text("loading..."); - } - if (snapshot.data is UssdFailed) { - return Text( - "Ussd Failed : \n" + snapshot.data!.toMap().toString()); - } - if (snapshot.data is EmptyState) { - return Text("Empty State"); - } +1. **Installation** - return Text("No state"); - }, - ), - ], - ), - ), - ); - } -} + Add `hover_ussd` to your `pubspec.yaml` file: + ```yaml + dependencies: + hover_ussd: ^latest_version + ``` -``` + Then, run: -## Customization + ```sh + flutter pub get + ``` -* ### To use your logo on the processing screen -```dart - HoverUssd.initialize( - branding: 'Hover Ussd Example', logo: "mipmap/ic_launcher"); -``` +2. **Initialization** + + Initialize HoverUssd with your Hover API key at [docs.usehover.com](https://docs.usehover.com/), branding, and logo information: + + ```dart + final HoverUssd _hoverUssd = HoverUssd(); + await _hoverUssd.initialize( + apiKey: "YOUR_API_KEY", + branding: "Your App Name", + logo: "ic_launcher", + notificationLogo: "ic_launcher", + ); + ``` + +3. **Start a Transaction** + + Start a USSD transaction with the provided parameters: + + ```dart + await _hoverUssd.startTransaction( + actionId: "ACTION_ID", + extras: {}, + theme: "myhoverTheme", //located in android/app/main/values/styles.xml + header: "Hover Ussd Example", + showUserStepDescriptions: true, + ); + ``` + +4. **Permissions Check** + + Check if the app has all the necessary permissions: + + ```dart + bool hasPermissions = await _hoverUssd.hasAllPermissions(); + ``` + +5. **Overlay and Accessibility Check** + + Check if the app has overlay and accessibility permissions: + + ```dart + bool isOverlayEnabled = await _hoverUssd.isOverlayEnabled(); + bool isAccessibilityEnabled = await _hoverUssd.isAccessibilityEnabled(); + ``` + +6. **Retrieve Actions** + + Retrieve a list of available actions: + + ```dart + List actions = await _hoverUssd.getAllActions(); + ``` + +7. **Refresh Actions** + + Refresh actions to get the latest updates: + + ```dart + await _hoverUssd.refreshActions(); + ``` + +8. **Listen to Transaction States** + + Get a stream of transaction states: + + ```dart + Stream transactionStream = _hoverUssd.getUssdTransactionState(); + ``` + +9. **Listen to Download Action States** + + Get a stream of download action states: + + ```dart + Stream downloadStream = _hoverUssd.getDownloadActionState(); + ``` + +## Customization * ### To use your own theme style @@ -156,23 +173,234 @@ class MyApp extends StatelessWidget { ``` -#### add the theme name when calling **HoverUssd().startTransaction()** -```dart - _hoverUssd.startTransaction( - theme: "myHoverTheme",); +* ### Customize an hover session +```dart + await _hoverUssd.startTransaction( + actionId: "ACTION_ID", + extras: {}, + theme: "myhoverTheme", // as in android/app/main/values/styles.xml + header: "Hover Ussd Example"// the title of your hover session, + showUserStepDescriptions: true, + ); ``` -* ### Others customizations +### Example + ```dart -startTransaction( - {required String actionId, - Map? extras, - String? theme, - String? header, - String? initialProcessingMessage, - int? finalMsgDisplayTime, - bool? showUserStepDescriptions}) +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key}); + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + late final HoverUssd _hoverUssd = HoverUssd(); + // Create an instance of HoverUssd you can pass it to a provider, + // or use get_it to make it available to the entire app + + late StreamSubscription _transactionListening; + late StreamSubscription _actionDownloadListening; + bool _hasPermissions = false; + bool _isOverlayEnabled = false; + bool _isAccessibilityEnabled = false; + + @override + void initState() { + _transactionListening = + _hoverUssd.getUssdTransactionState().listen((event) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(event.toMap().toString()))); + }); + + _actionDownloadListening = + _hoverUssd.getDownloadActionState().listen((event) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(event.toString()))); + }); + _checkAccessibility(); + _checkPermissions(); + _checkOverlay(); + + //initialize hover after the downlad listener is set + + _actionDownloadListening.onData((event) { + _initHover(); + }); + + super.initState(); + } + + @override + void dispose() { + _transactionListening.cancel(); + _actionDownloadListening.cancel(); + super.dispose(); + } + //check for permissions + + Future _checkPermissions() async { + _hasPermissions = await _hoverUssd.hasAllPermissions(); + setState(() {}); + } + + Future _checkAccessibility() async { + _isAccessibilityEnabled = await _hoverUssd.isAccessibilityEnabled(); + setState(() {}); + } + + Future _checkOverlay() async { + _isOverlayEnabled = await _hoverUssd.isOverlayEnabled(); + setState(() {}); + } + + Future _initHover() async { + try { + await _hoverUssd.initialize( + apiKey: "15ccc2bd81801d8c5fbfd5847d3b4e77", + branding: "My Hover App", + logo: "ic_launcher", + notificationLogo: "ic_launcher", + ); + } catch (e) { + print(e); + } + } + + Future _getAndGoToActions() async { + final actions = await _hoverUssd.getAllActions(); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => HoverActionListPage( + hoverActions: actions, + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Hover Ussd Example'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () { + _hoverUssd.startTransaction( + actionId: "c6e45e62", + extras: {"price": "4000"}, + theme: "HoverTheme", + header: "Hover Ussd Example", + showUserStepDescriptions: true, + ); + }, + child: const Text("Start Transaction"), + ), + ElevatedButton( + onPressed: _getAndGoToActions, + child: const Text("Get Actions"), + ), + + //refresh actions + ElevatedButton( + onPressed: () { + _hoverUssd.refreshActions(); + }, + child: const Text("Refresh Actions"), + ), + Text( + _hasPermissions + ? "Permissions Granted" + : "Permissions Not Granted", + style: TextStyle( + color: _hasPermissions ? Colors.green : Colors.red, + fontWeight: FontWeight.bold, + ), + ), + Text( + _isAccessibilityEnabled + ? "Accessibility Granted" + : "Accessibility Not Granted", + style: TextStyle( + color: _isAccessibilityEnabled ? Colors.green : Colors.red, + fontWeight: FontWeight.bold, + ), + ), + Text( + _isOverlayEnabled ? "Overlay Granted" : "Overlay Not Granted", + style: TextStyle( + color: _isOverlayEnabled ? Colors.green : Colors.red, + fontWeight: FontWeight.bold, + ), + ), + + StreamBuilder( + stream: _hoverUssd.getDownloadActionState(), + builder: (context, snapshot) { + final state = snapshot.data; + + String statusText; + Color textColor; + + if (state is ActionDownloaded) { + statusText = "Actions Downloaded"; + textColor = Colors.green; + } else if (state is ActionDownloadFailed) { + statusText = "Actions Not Downloaded"; + textColor = Colors.red; + } else if (state is ActionDownloading) { + statusText = "Actions Downloading"; + textColor = + Colors.blue; // Adjust color as per your preference + } else { + statusText = "Action Download Status: Unknown"; + textColor = Colors.grey; + } + + return Text( + statusText, + style: + TextStyle(color: textColor, fontWeight: FontWeight.bold), + ); + }, + ), + StreamBuilder( + stream: _hoverUssd.getUssdTransactionState(), + builder: (BuildContext context, snapshot) { + if (snapshot.data is SmsParsed) { + return Text("Sms parsed : \n${snapshot.data!.toMap()}"); + } + if (snapshot.data is UssdSucceeded) { + return Text("Ussd Succeded : \n${snapshot.data!.toMap()}"); + } + if (snapshot.data is UssdLoading) { + return const Text("loading..."); + } + if (snapshot.data is UssdFailed) { + return Text("Ussd Failed : \n${snapshot.data!.toMap()}"); + } + if (snapshot.data is EmptyState) { + return const Text("Empty State"); + } + return const Text("No state"); + }, + ), + ], + ), + ), + ); + } +} + + ``` + ## Important * **This is a unofficial plugin** ## Credit diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index 1fd20b4..19aca76 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -1,38 +1,28 @@ package com.lucdotdev.hover_ussd; - import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; - -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - import com.hover.sdk.actions.HoverAction; import com.hover.sdk.api.Hover; import com.hover.sdk.api.HoverParameters; import com.hover.sdk.database.HoverRoomDatabase; import com.hover.sdk.transactions.Transaction; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import io.flutter.plugin.common.EventChannel; - public class HoverUssdApi { - private final Activity activity; private final Context context; - - public HoverUssdApi(Activity activity) { + public HoverUssdApi(Activity activity, Context context) { this.activity = activity; - this.context = activity.getApplicationContext(); + this.context = context; } public void initialize(String apiKey, String branding, String logo, String notificationLogo, Hover.DownloadListener downloadListener) { @@ -41,7 +31,6 @@ public void initialize(String apiKey, String branding, String logo, String notif int logoResourceId = getResourceId(logo == null ? "ic_launcher" : logo); int notificationLogoResourceId = getResourceId(notificationLogo == null ? "ic_launcher" : notificationLogo); Hover.setBranding(branding == null ? "Hover Ussd Plugin" : branding, logoResourceId, notificationLogoResourceId, context); - } public boolean hasAllPerms() { @@ -56,7 +45,6 @@ public boolean isOverlayEnabled() { return Hover.isOverlayEnabled(context); } - public ArrayList> getAllActions() { List actions = HoverRoomDatabase.getInstance(context).actionDao().getAll(); ArrayList> mapActions = new ArrayList<>(); @@ -69,26 +57,20 @@ public ArrayList> getAllActions() { return mapActions; } - public void refreshActions(Hover.DownloadListener actionDownloadListener) { Hover.updateActionConfigs(actionDownloadListener, context); } - - public ArrayList> getAllTransaction() { - List transactions = HoverRoomDatabase.getInstance(context).transactionDao().getAll(); ArrayList> mapTransactions = new ArrayList<>(); for (Transaction transaction : transactions) { - Map mapTransaction = HoverUssdObjectToMap.convertTransactionToMap(transaction); mapTransactions.add(mapTransaction); } return mapTransactions; - } private int getResourceId(String resourceName) { @@ -99,6 +81,7 @@ private int getResourceId(String resourceName) { return 0; } } + public void sendUssd(String action_id, HashMap extra, String theme, @@ -106,10 +89,7 @@ public void sendUssd(String action_id, String initialProcessingMessage, boolean showUserStepDescriptions, int finalMsgDisplayTime, - BroadcastReceiver transactionStateReceiver - - ) { - + BroadcastReceiver transactionStateReceiver) { final HoverParameters.Builder builder = new HoverParameters.Builder(context); @@ -123,11 +103,9 @@ public void sendUssd(String action_id, } } - if (theme != null) { int id = context.getResources().getIdentifier(theme, "style", context.getPackageName()); builder.style(id); - } if (header != null) { builder.setHeader(header); @@ -138,17 +116,11 @@ public void sendUssd(String action_id, builder.showUserStepDescriptions(showUserStepDescriptions); - if (finalMsgDisplayTime != 0) { builder.finalMsgDisplayTime(finalMsgDisplayTime); } - Intent buildIntent = builder.buildIntent(); - - - activity.startActivityForResult(buildIntent,0 ); - - + activity.startActivityForResult(buildIntent, 0); } } diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index 10516b7..51e3e27 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -34,36 +34,27 @@ public class HoverUssdPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener { - private Activity activity; - - private EventChannel.EventSink transactionEventSink; private EventChannel.EventSink actionDownloadEventSink; private BroadcastReceiver smsReceiver; private BroadcastReceiver transactionStateReceiver; - private HoverUssdApi hoverUssdApi; - + private Context context; private static final String TAG = "HOVER_USSD_PLUGIN"; - @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - MethodChannel channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "HoverUssdChannel"); EventChannel transactionEventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "TransactionEvent"); EventChannel actionDownloadEventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "ActionDownloadEvent"); + context = flutterPluginBinding.getApplicationContext(); + actionDownloadEventChannel.setStreamHandler(new EventChannel.StreamHandler() { @Override public void onListen(Object arguments, EventChannel.EventSink events) { actionDownloadEventSink = events; - - Map result = new HashMap<>(); - result.put("state", "listening"); - - actionDownloadEventSink.success(result); } @Override @@ -82,45 +73,66 @@ public void onCancel(Object arguments) { } }); - channel.setMethodCallHandler(this); - } @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + } + @Override + public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { + activity = binding.getActivity(); + binding.addActivityResultListener(this); + smsReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + handleSmsReceived(intent); + } + }; + LocalBroadcastManager.getInstance(activity).registerReceiver(smsReceiver, new IntentFilter(activity.getPackageName() + ".SMS_MISS")); } - private String intentNullAwareString(Intent intent, String name) { - return intent.hasExtra(name) ? intent.getStringExtra(name) : ""; + @Override + public void onDetachedFromActivityForConfigChanges() { + activity = null; } + @Override + public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { + activity = binding.getActivity(); + binding.addActivityResultListener(this); + } + + @Override + public void onDetachedFromActivity() { + LocalBroadcastManager.getInstance(activity).unregisterReceiver(smsReceiver); + if (transactionStateReceiver != null) { + LocalBroadcastManager.getInstance(activity.getApplication()).unregisterReceiver(transactionStateReceiver); + } + activity = null; + } + + @Override + public boolean onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == 0) { + if (resultCode == Activity.RESULT_OK && data != null) { + handleUssdSuccess(data); + } else if (resultCode == Activity.RESULT_CANCELED && data != null) { + handleUssdFailed(data); + } + return true; + } + return false; + } @Override public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - hoverUssdApi = new HoverUssdApi(activity); + hoverUssdApi = new HoverUssdApi(activity, context); switch (call.method) { case "Initialize": - hoverUssdApi.initialize(call.argument("apiKey"), call.argument("branding"), call.argument("logo"), call.argument("notificationLogo"), new Hover.DownloadListener() { - @Override - public void onError(String s) { - Map result = new HashMap<>(); - result.put("state", "actionDownloadFailed"); - result.put("error", s); - actionDownloadEventSink.success(result); - } - - @Override - public void onSuccess(ArrayList arrayList) { - Map result = new HashMap<>(); - result.put("state", "actionDownloaded"); - result.put("isDownloaded", true); - actionDownloadEventSink.success(result); - } - }); - + initializeHover(call); break; case "HasAllPermissions": result.success(hoverUssdApi.hasAllPerms()); @@ -138,173 +150,131 @@ public void onSuccess(ArrayList arrayList) { result.success(hoverUssdApi.getAllTransaction()); break; case "refreshActions": - hoverUssdApi.refreshActions(new Hover.DownloadListener() { - @Override - public void onError(String s) { - Map result = new HashMap<>(); - result.put("state", "actionDownloadFailed"); - result.put("error", s); - actionDownloadEventSink.success(result); - } - - @Override - public void onSuccess(ArrayList arrayList) { - Map result = new HashMap<>(); - result.put("state", "actionDownloaded"); - result.put("isDownloaded", true); - actionDownloadEventSink.success(result); - } - }); + refreshActions(); break; - case "HoverStartATransaction": - Map resultJson = new HashMap<>(); - resultJson.put("state", "ussdLoading"); - transactionEventSink.success(resultJson); - - transactionStateReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent i) { - - transactionEventSink.success(i); - Log.i(TAG, "Received pending transaction created broadcast"); - Log.i(TAG, "uuid: " + i.getStringExtra("uuid")); - - - } - }; - - try { - hoverUssdApi.sendUssd( - (String) call.argument("actionId"), - call.hasArgument("extras") ? - Objects.requireNonNull(call.argument("extras")) - : new HashMap(), - call.hasArgument("theme") ? - (String) call.argument("theme") : - "", - call.hasArgument("header") ? - (String) call.argument("header") - : "", - call.hasArgument("initialProcessingMessage") ? - (String) call.argument("initialProcessingMessage") - : "", - false, - (int) call.argument("finalMsgDisplayTime"), - transactionStateReceiver - ); - } catch (Exception e) { - - Map resultError = new HashMap<>(); - resultError.put("state", "ussdFailed"); - - resultError.put("errorMessage", e.getMessage()); - - transactionEventSink.success(resultError); - - } - - result.success(true); - + startHoverTransaction(call, result); break; default: result.notImplemented(); - } hoverUssdApi = null; - } - - @Override - public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { - activity = binding.getActivity(); - binding.addActivityResultListener(this); - smsReceiver = new BroadcastReceiver() { + private void initializeHover(MethodCall call) { + hoverUssdApi.initialize(call.argument("apiKey"), call.argument("branding"), call.argument("logo"), call.argument("notificationLogo"), new Hover.DownloadListener() { @Override - public void onReceive(Context context, Intent intent) { + public void onError(String s) { Map result = new HashMap<>(); - result.put("state", "smsParsed"); - result.put("action_id", intentNullAwareString(intent, "action_id")); - result.put("response_message", intentNullAwareString(intent, "response_message")); - result.put("status", intentNullAwareString(intent, "status")); - result.put("status_meaning", intentNullAwareString(intent, "status_meaning")); - result.put("status_description", intentNullAwareString(intent, "status_description")); - result.put("uuid", intentNullAwareString(intent, "uuid")); - result.put("im_hni", intentNullAwareString(intent, "im_hni")); - result.put("environment", intent.getIntExtra("environment", 0)); - result.put("request_timestamp", intent.getIntExtra("request_timestamp", 0)); - result.put("response_timestamp", intent.getIntExtra("response_timestamp", 0)); - - result.put("matched_parser_id", intentNullAwareString(intent, "matched_parser_id")); - result.put("messagetype", intentNullAwareString(intent, "messagetype")); - result.put("message_sender", intentNullAwareString(intent, "message_sender")); - result.put("regex", intentNullAwareString(intent, "regex")); - - - transactionEventSink.success(result); + result.put("state", "actionDownloadFailed"); + result.put("error", s); + actionDownloadEventSink.success(result); } - }; - LocalBroadcastManager.getInstance(activity.getBaseContext()).registerReceiver(smsReceiver, new IntentFilter(activity.getPackageName() + ".SMS_MISS")); - - - } - - @Override - public void onDetachedFromActivityForConfigChanges() { - activity = null; - } - - - @Override - public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { - activity = binding.getActivity(); - binding.addActivityResultListener(this); + @Override + public void onSuccess(ArrayList arrayList) { + Map result = new HashMap<>(); + result.put("state", "actionDownloaded"); + result.put("isDownloaded", true); + actionDownloadEventSink.success(result); + } + }); } - @Override - public void onDetachedFromActivity() { - LocalBroadcastManager.getInstance(activity.getBaseContext()).unregisterReceiver(smsReceiver); - - if (transactionStateReceiver != null) { - LocalBroadcastManager.getInstance(activity.getApplication()).unregisterReceiver(transactionStateReceiver); - } - - activity = null; + private void refreshActions() { + hoverUssdApi.refreshActions(new Hover.DownloadListener() { + @Override + public void onError(String s) { + Map result = new HashMap<>(); + result.put("state", "actionDownloadFailed"); + result.put("error", s); + actionDownloadEventSink.success(result); + } + @Override + public void onSuccess(ArrayList arrayList) { + Map result = new HashMap<>(); + result.put("state", "actionDownloaded"); + result.put("isDownloaded", true); + actionDownloadEventSink.success(result); + } + }); } + private void startHoverTransaction(MethodCall call, MethodChannel.Result result) { + Map resultJson = new HashMap<>(); + resultJson.put("state", "ussdLoading"); + transactionEventSink.success(resultJson); - @Override - public boolean onActivityResult(int requestCode, int resultCode, Intent data) { - - - if (requestCode == 0 && data != null && resultCode == Activity.RESULT_OK) { - String uuid = data.hasExtra("uuid") ? data.getStringExtra("uuid") : ""; - - Map result = new HashMap<>(); - result.put("state", "ussdSucceeded"); - result.put("uuid", uuid); - if (data.hasExtra("session_messages")) { - String[] sessionMessages = data.getStringArrayExtra("session_messages"); - result.put("ussdSessionMessages", sessionMessages); + transactionStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent i) { + transactionEventSink.success(i.getExtras()); + Log.i(TAG, "Received pending transaction created broadcast"); + Log.i(TAG, "uuid: " + i.getStringExtra("uuid")); } + }; - transactionEventSink.success(result); + try { + hoverUssdApi.sendUssd( + Objects.requireNonNull(call.argument("actionId")), + call.argument("extras") != null ? call.argument("extras") : new HashMap(), + call.argument("theme"), + call.argument("header"), + call.argument("initialProcessingMessage"), + false, + call.argument("finalMsgDisplayTime"), + transactionStateReceiver + ); + result.success(true); + } catch (Exception e) { + Map resultError = new HashMap<>(); + resultError.put("state", "ussdFailed"); + resultError.put("errorMessage", e.getMessage()); + transactionEventSink.success(resultError); + } + } - return true; + private void handleSmsReceived(Intent intent) { + Map result = new HashMap<>(); + result.put("state", "smsParsed"); + result.put("action_id", intentNullAwareString(intent, "action_id")); + result.put("response_message", intentNullAwareString(intent, "response_message")); + result.put("status", intentNullAwareString(intent, "status")); + result.put("status_meaning", intentNullAwareString(intent, "status_meaning")); + result.put("status_description", intentNullAwareString(intent, "status_description")); + result.put("uuid", intentNullAwareString(intent, "uuid")); + result.put("im_hni", intentNullAwareString(intent, "im_hni")); + result.put("environment", intent.getIntExtra("environment", 0)); + result.put("request_timestamp", intent.getIntExtra("request_timestamp", 0)); + result.put("response_timestamp", intent.getIntExtra("response_timestamp", 0)); + result.put("matched_parser_id", intentNullAwareString(intent, "matched_parser_id")); + result.put("messagetype", intentNullAwareString(intent, "messagetype")); + result.put("message_sender", intentNullAwareString(intent, "message_sender")); + result.put("regex", intentNullAwareString(intent, "regex")); + transactionEventSink.success(result); + } - } else if (requestCode == 0 && resultCode == Activity.RESULT_CANCELED && data != null) { - Map result = new HashMap<>(); - result.put("state", "ussdFailed"); - result.put("errorMessage", data.getStringExtra("error")); - transactionEventSink.success(result); - return true; + private void handleUssdSuccess(Intent data) { + String uuid = data.hasExtra("uuid") ? data.getStringExtra("uuid") : ""; + Map result = new HashMap<>(); + result.put("state", "ussdSucceeded"); + result.put("uuid", uuid); + if (data.hasExtra("session_messages")) { + String[] sessionMessages = data.getStringArrayExtra("session_messages"); + result.put("ussdSessionMessages", sessionMessages); } + transactionEventSink.success(result); + } - return true; - + private void handleUssdFailed(Intent data) { + Map result = new HashMap<>(); + result.put("state", "ussdFailed"); + result.put("errorMessage", data.getStringExtra("error")); + transactionEventSink.success(result); } + private String intentNullAwareString(Intent intent, String name) { + return intent.hasExtra(name) ? intent.getStringExtra(name) : ""; + } } \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index a150154..63545d1 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -35,8 +35,8 @@ class _MyHomePageState extends State { late StreamSubscription _transactionListening; late StreamSubscription _actionDownloadListening; bool _hasPermissions = false; - bool isOverlayEnabled = false; - bool isAccessiblityEnabled = false; + bool _isOverlayEnabled = false; + bool _isAccessibilityEnabled = false; @override void initState() { @@ -51,13 +51,13 @@ class _MyHomePageState extends State { ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text(event.toString()))); }); - + _checkAccessibility(); + _checkPermissions(); + _checkOverlay(); _actionDownloadListening.onData((event) { _initHover(); }); - _checkPermissions(); - _isAccessiblityEnabled(); - _isOverlayEnabled(); + super.initState(); } @@ -67,21 +67,21 @@ class _MyHomePageState extends State { _actionDownloadListening.cancel(); super.dispose(); } + //check for permissions Future _checkPermissions() async { - bool hasPermissions = await _hoverUssd.hasAllPermissions(); - setState(() { - _hasPermissions = hasPermissions; - }); + _hasPermissions = await _hoverUssd.hasAllPermissions(); + setState(() {}); } - Future _isAccessiblityEnabled() async { - bool isAccessiblityEnabled = await _hoverUssd.isAccessiblityEnabled(); - print(isAccessiblityEnabled); + + Future _checkAccessibility() async { + _isAccessibilityEnabled = await _hoverUssd.isAccessibilityEnabled(); + setState(() {}); } - Future _isOverlayEnabled() async { - bool isOverlayEnabled = await _hoverUssd.isOverlayEnabled(); - print(isOverlayEnabled); + Future _checkOverlay() async { + _isOverlayEnabled = await _hoverUssd.isOverlayEnabled(); + setState(() {}); } Future _initHover() async { @@ -99,9 +99,6 @@ class _MyHomePageState extends State { Future _getAndGoToActions() async { final actions = await _hoverUssd.getAllActions(); - for (var action in actions) { - print(action.toMap()); - } Navigator.push( context, MaterialPageRoute( @@ -157,20 +154,18 @@ class _MyHomePageState extends State { ), ), Text( - isAccessiblityEnabled + _isAccessibilityEnabled ? "Accessibility Granted" : "Accessibility Not Granted", style: TextStyle( - color: isAccessiblityEnabled ? Colors.green : Colors.red, + color: _isAccessibilityEnabled ? Colors.green : Colors.red, fontWeight: FontWeight.bold, ), ), Text( - isOverlayEnabled - ? "Overlay Granted" - : "Overlay Not Granted", + _isOverlayEnabled ? "Overlay Granted" : "Overlay Not Granted", style: TextStyle( - color: isOverlayEnabled ? Colors.green : Colors.red, + color: _isOverlayEnabled ? Colors.green : Colors.red, fontWeight: FontWeight.bold, ), ), @@ -211,7 +206,6 @@ class _MyHomePageState extends State { if (snapshot.data is SmsParsed) { return Text("Sms parsed : \n${snapshot.data!.toMap()}"); } - if (snapshot.data is UssdSucceeded) { return Text("Ussd Succeded : \n${snapshot.data!.toMap()}"); } @@ -224,7 +218,6 @@ class _MyHomePageState extends State { if (snapshot.data is EmptyState) { return const Text("Empty State"); } - return const Text("No state"); }, ), diff --git a/lib/hover_ussd_plugin.dart b/lib/hover_ussd_plugin.dart index d98b387..aeffa94 100644 --- a/lib/hover_ussd_plugin.dart +++ b/lib/hover_ussd_plugin.dart @@ -67,8 +67,8 @@ class HoverUssd { } // this check if app accessibility permission is granted - Future isAccessiblityEnabled() async { - return await _methodChannel.invokeMethod("IsAccessiblityEnabled"); + Future isAccessibilityEnabled() async { + return await _methodChannel.invokeMethod("IsAccessibilityEnabled"); } // check if app has overlay permission From 16f11d20f6d71969de715fa8fa1220dd2060735b Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Sun, 17 Mar 2024 20:29:51 +0200 Subject: [PATCH 34/36] add no Sms Version (playstore issues) and custom Activity set if possible --- README.md | 11 +++++++++++ android/build.gradle | 2 +- .../com/lucdotdev/hover_ussd/HoverUssdApi.java | 3 +++ .../com/lucdotdev/hover_ussd/HoverUssdPlugin.java | 4 ++++ example/lib/main.dart | 4 ++++ lib/hover_ussd_plugin.dart | 15 ++++++++++----- 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fe83681..4a21f72 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,17 @@ The Hover USSD plugin provides a simple and easy-to-use interface for integratin showUserStepDescriptions: true, ); ``` + +* ### Add a custom permissions activity + +```dart + + final String activityName = "com.example.yourApp.custompermissionsActivity"; + _hoverUssd.setPermissionsActivity(activityName: activityName); + + +``` + ### Example ```dart diff --git a/android/build.gradle b/android/build.gradle index 03ed6c4..4ce7165 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -44,7 +44,7 @@ android { dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.0.0' - implementation 'com.hover:android-sdk:2.0.0-beta04' + implementation 'com.hover:android-sdk:2.0.0-beta04-noSMS' } } diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index 19aca76..7268ebb 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -81,6 +81,9 @@ private int getResourceId(String resourceName) { return 0; } } + public void setPermissionsActivity(String activityString){ + Hover.setPermissionActivity(activityString, context); + } public void sendUssd(String action_id, HashMap extra, diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java index 51e3e27..a318a5b 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdPlugin.java @@ -137,6 +137,10 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result case "HasAllPermissions": result.success(hoverUssdApi.hasAllPerms()); break; + case "SetPermissionsActivity": + hoverUssdApi.setPermissionsActivity((String) call.argument("activityName")); + result.success(true); + break; case "IsAccessibilityEnabled": result.success(hoverUssdApi.isAccessibilityEnabled()); break; diff --git a/example/lib/main.dart b/example/lib/main.dart index 63545d1..9743385 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -38,6 +38,8 @@ class _MyHomePageState extends State { bool _isOverlayEnabled = false; bool _isAccessibilityEnabled = false; + final String activityName = "com.hover.sdk.permissions.PermissionActivity"; + @override void initState() { _transactionListening = @@ -92,6 +94,8 @@ class _MyHomePageState extends State { logo: "ic_launcher", notificationLogo: "ic_launcher", ); + + _hoverUssd.setPermissionsActivity(activityName: activityName); } catch (e) { print(e); } diff --git a/lib/hover_ussd_plugin.dart b/lib/hover_ussd_plugin.dart index aeffa94..aed31ef 100644 --- a/lib/hover_ussd_plugin.dart +++ b/lib/hover_ussd_plugin.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:hover_ussd/hover_ussd.dart'; -import 'models/download_action_state.dart'; class HoverUssd { static const MethodChannel _methodChannel = MethodChannel('HoverUssdChannel'); @@ -66,12 +65,18 @@ class HoverUssd { return await _methodChannel.invokeMethod("HasAllPermissions"); } - // this check if app accessibility permission is granted + /// this check if app accessibility permission is granted Future isAccessibilityEnabled() async { - return await _methodChannel.invokeMethod("IsAccessibilityEnabled"); + return await _methodChannel. + invokeMethod("IsAccessibilityEnabled"); + } + /// this set custom permissions activity for the app + /// [activityName] is the name of the custom activity to be set ex: 'com.example.example.CustomPermissionsActivity' + Future setPermissionsActivity({required String activityName})async{ + _methodChannel.invokeMethod("SetPermissionsActivity"); } - // check if app has overlay permission + /// check if app has overlay permission Future isOverlayEnabled() async { return await _methodChannel.invokeMethod("IsOverlayEnabled"); } @@ -99,7 +104,7 @@ class HoverUssd { } } - //refresh actions + /// refresh actions Future refreshActions() async { await _methodChannel.invokeMethod("refreshActions"); } From 3f4dc8c08be87cd6f9c30f91095c17af745fc8df Mon Sep 17 00:00:00 2001 From: lucdotdev Date: Sun, 12 May 2024 00:21:04 +0200 Subject: [PATCH 35/36] fix typos --- .idea/libraries/Dart_SDK.xml | 32 +- .../shelved.patch | 329 ------------------ ..._02_03_2024_06_35__Default_Changelist_.xml | 4 - .idea/workspace.xml | 68 +--- .../lucdotdev/hover_ussd/HoverUssdApi.java | 35 +- .../lucdotdev/hover_ussd/HoverUssdPlugin.java | 24 +- example/lib/main.dart | 6 +- lib/hover_ussd_plugin.dart | 63 ++-- 8 files changed, 131 insertions(+), 430 deletions(-) delete mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35_[Default_Changelist]/shelved.patch delete mode 100644 .idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35__Default_Changelist_.xml diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml index 312f576..449c2cb 100644 --- a/.idea/libraries/Dart_SDK.xml +++ b/.idea/libraries/Dart_SDK.xml @@ -1,17 +1,27 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35_[Default_Changelist]/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35_[Default_Changelist]/shelved.patch deleted file mode 100644 index f59518c..0000000 --- a/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35_[Default_Changelist]/shelved.patch +++ /dev/null @@ -1,329 +0,0 @@ -Index: lib/hover_ussd.dart -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>import 'dart:async';\r\nimport 'package:flutter/services.dart';\r\n\r\nenum TransactionState {\r\n // when the ussd code run succesfully\r\n\r\n succesfull,\r\n // when the ussd code failed; this can be caused by user\r\n // dissmiss or request refuse\r\n failed,\r\n // this is when hover failed to dowload the list of your\r\n // action , usualy when the app is not connected\r\n actionDowaloadFailed\r\n}\r\n\r\nclass HoverUssd {\r\n static const MethodChannel _methodChannel = MethodChannel('HoverUssdChannel');\r\n static const EventChannel _eventChannel = EventChannel('TransactionEvent');\r\n\r\n static Future initialize({String? branding, String? logo}) async {\r\n await _methodChannel.invokeMethod(\"Initialize\",\r\n {\"branding\": branding ?? \"Flutter App\", \"logo\": logo ?? \"ic_launcher\"});\r\n }\r\n\r\n Stream? _onTransactionStateChanged;\r\n\r\n // when you call send ussd, automatically the ussd procces begin\r\n // and the permission is autaumatically handle\r\n // this method came with 3 params\r\n // @actionId is the id for the action you trying to call\r\n // cc: https://docs.usehover.com/ussd\r\n // @extras is the extras variable which came with action id , you can\r\n // convigure tha on : https://www.usehover.com/\r\n // optional: if you want to customize the ussd theme then you can add a theme at\r\n // your style.xml and then add the style string on @theme param\r\n Future sendUssd(\r\n {required String actionId,\r\n Map? extras,\r\n String? theme}) async =>\r\n await _methodChannel.invokeMethod(\"HoverStartATransaction\",\r\n {\"actionId\": actionId, \"extras\": extras ?? {}, \"theme\": theme});\r\n\r\n // this is for getting response of the current ussd session\r\n\r\n Stream? get getUssdTransactionState {\r\n if (_onTransactionStateChanged == null) {\r\n _onTransactionStateChanged = _eventChannel\r\n .receiveBroadcastStream()\r\n .map((dynamic event) => _parseTransactionState(event));\r\n }\r\n return _onTransactionStateChanged;\r\n }\r\n\r\n TransactionState _parseTransactionState(String? state) {\r\n switch (state) {\r\n case \"success\":\r\n return TransactionState.succesfull;\r\n case \"failed\":\r\n return TransactionState.failed;\r\n case \"actionDownloadFailed\":\r\n return TransactionState.actionDowaloadFailed;\r\n default:\r\n throw ArgumentError('$state');\r\n }\r\n }\r\n}\r\n -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/lib/hover_ussd.dart b/lib/hover_ussd.dart ---- a/lib/hover_ussd.dart (revision ef28cc9dd4019341ab6a14710a361884e6bd2ec0) -+++ b/lib/hover_ussd.dart (date 1709327262151) -@@ -24,13 +24,13 @@ - - Stream? _onTransactionStateChanged; - -- // when you call send ussd, automatically the ussd procces begin -- // and the permission is autaumatically handle -+ // when you call send ussd, automatically the ussd process begin -+ // and the permission is automatically handle - // this method came with 3 params - // @actionId is the id for the action you trying to call - // cc: https://docs.usehover.com/ussd - // @extras is the extras variable which came with action id , you can -- // convigure tha on : https://www.usehover.com/ -+ // configure tha on : https://www.usehover.com/ - // optional: if you want to customize the ussd theme then you can add a theme at - // your style.xml and then add the style string on @theme param - Future sendUssd( -Index: pubspec.lock -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+># Generated by pub\r\n# See https://dart.dev/tools/pub/glossary#lockfile\r\npackages:\r\n async:\r\n dependency: transitive\r\n description:\r\n name: async\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"2.8.1\"\r\n boolean_selector:\r\n dependency: transitive\r\n description:\r\n name: boolean_selector\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"2.1.0\"\r\n characters:\r\n dependency: transitive\r\n description:\r\n name: characters\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.1.0\"\r\n charcode:\r\n dependency: transitive\r\n description:\r\n name: charcode\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.3.1\"\r\n clock:\r\n dependency: transitive\r\n description:\r\n name: clock\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.1.0\"\r\n collection:\r\n dependency: transitive\r\n description:\r\n name: collection\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.15.0\"\r\n fake_async:\r\n dependency: transitive\r\n description:\r\n name: fake_async\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.2.0\"\r\n flutter:\r\n dependency: \"direct main\"\r\n description: flutter\r\n source: sdk\r\n version: \"0.0.0\"\r\n flutter_test:\r\n dependency: \"direct dev\"\r\n description: flutter\r\n source: sdk\r\n version: \"0.0.0\"\r\n matcher:\r\n dependency: transitive\r\n description:\r\n name: matcher\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"0.12.10\"\r\n meta:\r\n dependency: \"direct main\"\r\n description:\r\n name: meta\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.7.0\"\r\n path:\r\n dependency: transitive\r\n description:\r\n name: path\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.8.0\"\r\n sky_engine:\r\n dependency: transitive\r\n description: flutter\r\n source: sdk\r\n version: \"0.0.99\"\r\n source_span:\r\n dependency: transitive\r\n description:\r\n name: source_span\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.8.1\"\r\n stack_trace:\r\n dependency: transitive\r\n description:\r\n name: stack_trace\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.10.0\"\r\n stream_channel:\r\n dependency: transitive\r\n description:\r\n name: stream_channel\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"2.1.0\"\r\n string_scanner:\r\n dependency: transitive\r\n description:\r\n name: string_scanner\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.1.0\"\r\n term_glyph:\r\n dependency: transitive\r\n description:\r\n name: term_glyph\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.2.0\"\r\n test_api:\r\n dependency: transitive\r\n description:\r\n name: test_api\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"0.4.2\"\r\n typed_data:\r\n dependency: transitive\r\n description:\r\n name: typed_data\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"1.3.0\"\r\n vector_math:\r\n dependency: transitive\r\n description:\r\n name: vector_math\r\n url: \"https://pub.dartlang.org\"\r\n source: hosted\r\n version: \"2.1.0\"\r\nsdks:\r\n dart: \">=2.12.0 <3.0.0\"\r\n flutter: \">=1.20.0\"\r\n -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/pubspec.lock b/pubspec.lock ---- a/pubspec.lock (revision ef28cc9dd4019341ab6a14710a361884e6bd2ec0) -+++ b/pubspec.lock (date 1709327201783) -@@ -5,51 +5,50 @@ - dependency: transitive - description: - name: async -- url: "https://pub.dartlang.org" -+ sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" -+ url: "https://pub.dev" - source: hosted -- version: "2.8.1" -+ version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector -- url: "https://pub.dartlang.org" -+ sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" -+ url: "https://pub.dev" - source: hosted -- version: "2.1.0" -+ version: "2.1.1" - characters: - dependency: transitive - description: - name: characters -- url: "https://pub.dartlang.org" -+ sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" -+ url: "https://pub.dev" - source: hosted -- version: "1.1.0" -- charcode: -- dependency: transitive -- description: -- name: charcode -- url: "https://pub.dartlang.org" -- source: hosted -- version: "1.3.1" -+ version: "1.3.0" - clock: - dependency: transitive - description: - name: clock -- url: "https://pub.dartlang.org" -+ sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf -+ url: "https://pub.dev" - source: hosted -- version: "1.1.0" -+ version: "1.1.1" - collection: - dependency: transitive - description: - name: collection -- url: "https://pub.dartlang.org" -+ sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 -+ url: "https://pub.dev" - source: hosted -- version: "1.15.0" -+ version: "1.17.2" - fake_async: - dependency: transitive - description: - name: fake_async -- url: "https://pub.dartlang.org" -+ sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" -+ url: "https://pub.dev" - source: hosted -- version: "1.2.0" -+ version: "1.3.1" - flutter: - dependency: "direct main" - description: flutter -@@ -64,23 +63,34 @@ - dependency: transitive - description: - name: matcher -- url: "https://pub.dartlang.org" -+ sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" -+ url: "https://pub.dev" - source: hosted -- version: "0.12.10" -+ version: "0.12.16" -+ material_color_utilities: -+ dependency: transitive -+ description: -+ name: material_color_utilities -+ sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" -+ url: "https://pub.dev" -+ source: hosted -+ version: "0.5.0" - meta: - dependency: "direct main" - description: - name: meta -- url: "https://pub.dartlang.org" -+ sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" -+ url: "https://pub.dev" - source: hosted -- version: "1.7.0" -+ version: "1.9.1" - path: - dependency: transitive - description: - name: path -- url: "https://pub.dartlang.org" -+ sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" -+ url: "https://pub.dev" - source: hosted -- version: "1.8.0" -+ version: "1.8.3" - sky_engine: - dependency: transitive - description: flutter -@@ -90,58 +100,66 @@ - dependency: transitive - description: - name: source_span -- url: "https://pub.dartlang.org" -+ sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" -+ url: "https://pub.dev" - source: hosted -- version: "1.8.1" -+ version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace -- url: "https://pub.dartlang.org" -+ sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 -+ url: "https://pub.dev" - source: hosted -- version: "1.10.0" -+ version: "1.11.0" - stream_channel: - dependency: transitive - description: - name: stream_channel -- url: "https://pub.dartlang.org" -+ sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" -+ url: "https://pub.dev" - source: hosted -- version: "2.1.0" -+ version: "2.1.1" - string_scanner: - dependency: transitive - description: - name: string_scanner -- url: "https://pub.dartlang.org" -+ sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" -+ url: "https://pub.dev" - source: hosted -- version: "1.1.0" -+ version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph -- url: "https://pub.dartlang.org" -+ sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 -+ url: "https://pub.dev" - source: hosted -- version: "1.2.0" -+ version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api -- url: "https://pub.dartlang.org" -+ sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" -+ url: "https://pub.dev" - source: hosted -- version: "0.4.2" -- typed_data: -+ version: "0.6.0" -+ vector_math: - dependency: transitive - description: -- name: typed_data -- url: "https://pub.dartlang.org" -+ name: vector_math -+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" -+ url: "https://pub.dev" - source: hosted -- version: "1.3.0" -- vector_math: -+ version: "2.1.4" -+ web: - dependency: transitive - description: -- name: vector_math -- url: "https://pub.dartlang.org" -+ name: web -+ sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 -+ url: "https://pub.dev" - source: hosted -- version: "2.1.0" -+ version: "0.1.4-beta" - sdks: -- dart: ">=2.12.0 <3.0.0" -+ dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=1.20.0" -Index: .idea/workspace.xml -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n 1596904412123\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/.idea/workspace.xml b/.idea/workspace.xml ---- a/.idea/workspace.xml (revision ef28cc9dd4019341ab6a14710a361884e6bd2ec0) -+++ b/.idea/workspace.xml (date 1709327262323) -@@ -1,34 +1,51 @@ - - -+ -+ - - -- -- -- -- -- -- -+ -+ -+ -+ -+ - - -+ -+ - - - -+ -+ - - - -- -- -- -- -- -+ -+ - - - -Index: .idea/misc.xml -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP -<+>\r\n\r\n \r\n -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/.idea/misc.xml b/.idea/misc.xml ---- a/.idea/misc.xml (revision ef28cc9dd4019341ab6a14710a361884e6bd2ec0) -+++ b/.idea/misc.xml (date 1709353258729) -@@ -1,4 +1,6 @@ -- - - -+ -+ - -\ No newline at end of file diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35__Default_Changelist_.xml b/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35__Default_Changelist_.xml deleted file mode 100644 index 8addb6d..0000000 --- a/.idea/shelf/Uncommitted_changes_before_Checkout_at_02_03_2024_06_35__Default_Changelist_.xml +++ /dev/null @@ -1,4 +0,0 @@ - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 0955904..51433de 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,65 +5,13 @@ - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + \ No newline at end of file diff --git a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java index 830fcdb..e7bb5cd 100644 --- a/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java +++ b/android/src/main/java/com/lucdotdev/hover_ussd/HoverUssdApi.java @@ -31,7 +31,7 @@ public HoverUssdApi(Activity activity, Context context) { } public void initialize(String apiKey, String branding, String logo, String notificationLogo, Hover.DownloadListener downloadListener) { - Hover.initialize(context, apiKey, true, downloadListener); + Hover.initialize(context, apiKey, false, downloadListener); int logoResourceId = getResourceId(logo == null ? "ic_launcher" : logo); int notificationLogoResourceId = getResourceId(notificationLogo == null ? "ic_launcher" : notificationLogo);