I have a flutter app that needs to dial a ussd service with one sequence like :
*150*1*{secret}*1*{amount}*{gsmNumber}*{gsmNumber}#
I already tried all the packages from flutter none of them working in a proper way so I tried the telephony manger in android since it’s only android but none seem to be working
this is my mainActivity.kt
package com.example.topup
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.telephony.TelephonyManager.UssdResponseCallback
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
private val CHANNEL = "com.example.ussd"
private val REQUEST_CALL_PHONE = 1
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "callUssd") {
val ussdCode = call.argument<String>("ussdCode") ?: ""
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CALL_PHONE), REQUEST_CALL_PHONE)
result.error("PERMISSION_DENIED", "CALL_PHONE permission denied", null)
} else {
if (isSimPresent()) {
sendUssdRequest(ussdCode, result)
} else {
result.error("NO_SIM", "No SIM card present", null)
}
}
} else {
result.notImplemented()
}
}
}
private fun isSimPresent(): Boolean {
val subscriptionManager = getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
val activeSubscriptionInfoList = subscriptionManager.activeSubscriptionInfoList
val simPresent = activeSubscriptionInfoList != null && activeSubscriptionInfoList.isNotEmpty()
Log.d("USSD", "SIM present: $simPresent")
return simPresent
}
private fun sendUssdRequest(ussdCode: String, result: MethodChannel.Result) {
val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
try {
Log.d("USSD", "Sending USSD request: $ussdCode")
telephonyManager.sendUssdRequest(ussdCode, object : UssdResponseCallback() {
override fun onReceiveUssdResponse(
telephonyManager: TelephonyManager,
request: String,
response: CharSequence
) {
Log.d("USSD", "USSD request: $request")
Log.d("USSD", "USSD response received: $response")
result.success(response.toString())
}
override fun onReceiveUssdResponseFailed(
telephonyManager: TelephonyManager,
request: String,
failureCode: Int
) {
Log.e("USSD", "USSD request: $request")
Log.e("USSD", "USSD request failed with code: $failureCode")
when (failureCode) {
TelephonyManager.USSD_RETURN_FAILURE -> Log.e("USSD", "USSD_RETURN_FAILURE")
TelephonyManager.USSD_ERROR_SERVICE_UNAVAIL -> Log.e("USSD", "USSD_ERROR_SERVICE_UNAVAIL")
else -> Log.e("USSD", "Unknown failure code: $failureCode")
}
result.error("USSD_FAILED", "USSD request failed with code: $failureCode", null)
}
}, Handler(Looper.getMainLooper()))
} catch (e: SecurityException) {
Log.e("USSD", "SecurityException: ${e.message}")
result.error("USSD_FAILED", "SecurityException: ${e.message}", null)
} catch (e: Exception) {
Log.e("USSD", "Exception: ${e.message}")
result.error("USSD_FAILED", "Exception: ${e.message}", null)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CALL_PHONE) {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
Log.d("USSD", "CALL_PHONE permission granted")
} else {
Log.e("USSD", "CALL_PHONE permission denied")
}
}
}
}
and this is android.manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<application
android:label="topup"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
and this is the implementation in flutter :
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';
class UssdService extends StatelessWidget {
static const platform = MethodChannel('com.example.ussd');
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('USSD Service'),
),
body: Center(
child: ElevatedButton(
onPressed: _callUssd,
child: Text('Dial USSD Code'),
),
),
);
}
Future<void> _callUssd() async {
if (await _requestPhonePermission()) {
try {
final String result = await platform.invokeMethod('callUssd', {"ussdCode": "*123#"});
print('USSD Response: $result');
} on PlatformException catch (e) {
print('Failed to call USSD: ${e.message}');
}
} else {
print('Phone call permission not granted');
}
}
Future<bool> _requestPhonePermission() async {
PermissionStatus status = await Permission.phone.status;
if (!status.isGranted) {
status = await Permission.phone.request();
}
return status.isGranted;
}
}
when I try I get something like
`D/USSD ( 3110): Sending USSD request: *123#
E/USSD ( 3110): USSD request: *123#
E/USSD ( 3110): USSD request failed with code: -1
E/USSD ( 3110): USSD_RETURN_FAILURE
I/flutter ( 3110): Failed to dial USSD code. Error: PlatformException(USSD_FAILED, USSD request failed with code: -1, null, null)
`
Any one got any experience or can guide me through how to do the implementation for calling a USSD services in android flutter app