I am trying to get a phone number from the incoming call, and for Android, I've been trying to implement this answer, but I've been struggling because the answer is not full, has no example, and is very abstract, which makes it hard for a person without any knowledge about Platform Channel.
What I've tried: I've implemented pigeon code in a ServiceReceiver, declared a variable that I want to update and pass to the flutter code, however when I receive a call, the app doesn't even ask for any permission, which makes me think that the onReceive() function is not called when the phone receives an incoming call.
Here is the code:
main.dart:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import './output.dart';
void main() {
runApp(MyApp());
}
// void onClick() async {
// SearchRequest request = SearchRequest()..query = 'test';
// Api api = Api();
// SearchReply reply = await api.search(request);
// print('reply: ${reply.result}');
// }
class MyApp extends StatefulWidget {
// This widget is the root of your application.
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
static const platform = const MethodChannel('battery.ttsollution.com');
String _phoneNumber = '';
String _batteryLevel = 'Unknown battery level.';
void onClick() async {
SearchRequest request = SearchRequest()..query = 'test';
Api api = Api();
SearchReply reply = await api.search(request);
print('reply: ${reply.result}');
}
Future<void> _getPhoneNumber() async {
String phoneNumber;
try {
final int result = await platform.invokeMethod('getPhoneNumber');
phoneNumber = "Phone number: $result";
} catch (e) {
phoneNumber = "Failed to get the phone number due to: $e";
}
setState(() {
_phoneNumber = phoneNumber;
});
}
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
child: Text('Get Phone Number'),
onPressed: onClick,
),
Text(_batteryLevel),
],
),
),
),
);
}
}
MainActivity.java:
package com.example.phone_application_java;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.telephony.TelephonyManager;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "battery.ttsollution.com";
@RequiresApi(api = VERSION_CODES.M)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Pigeon.Api.setup(getFlutterEngine().getDartExecutor().getBinaryMessenger(), new ServiceReceiver.MyApi());
}
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler((call, result) -> {
// Note: this method is invoked on the main thread.
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
});
}
private int getBatteryLevel() {
int batteryLevel = -1;
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100)
/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
return batteryLevel;
}
}
ServiceReceiver.java:
package com.example.phone_application_java;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.os.Bundle;
import com.example.phone_application_java.Pigeon;
public class ServiceReceiver extends BroadcastReceiver {
static String phoneNumber = "Hiya";
static class MyApi implements Pigeon.Api {
@Override
public Pigeon.SearchReply search(Pigeon.SearchRequest arg) {
Pigeon.SearchReply reply = new Pigeon.SearchReply();
reply.setResult(ServiceReceiver.phoneNumber);
return reply;
}
}
@SuppressLint("UnsafeProtectedBroadcastReceiver")
@Override
public void onReceive(Context context, Intent intent) {
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
System.out.println("incomingNumber : " + incomingNumber);
phoneNumber = incomingNumber;
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
}
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.phone_application_java">
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<application android:label="phone_application_java" android:icon="@mipmap/ic_launcher">
<receiver android:name=".ServiceReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
<activity android:name=".MainActivity" 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" />
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="@drawable/launch_background" />
<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>
Flutter Doctor:
PS E:\Flutter Projects\phone_application_java> flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.0.6, on Microsoft Windows [Version 10.0.19042.928], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[√] Chrome - develop for the web
[√] Android Studio (version 4.1.0)
[√] Connected device (3 available)
• No issues found!