How to detect outgoing call number in Android Q - java

I'm unable to get outgoing call number in Android Q.
I've registered receiver in the manifest with this intent filter android.intent.action.NEW_OUTGOING_CALL and in code i'm detecting outgoing phone number like this
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL"))
String nr = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
}
But i can never get the outgoing call number in Android Q, is there a workaround to get this number differently or since Android Q it is completely impossible to detect outgoing call number?
Edit: It works with previous android versions

You need to add PROCESS_OUTGOING_CALLS permission
Create OutgoingCallReceiver
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
public class OutgoingCallReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
TelephonyManager tm = (TelephonyManager)context.getSystemService(Service.TELEPHONY_SERVICE);
if (tm.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK) {
String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
}
}
}
Add required permissions to read outcomming call in AndroidManifest file
<uses-permission android:name="android.permission.NEW_OUTGOING_CALL" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Request permissions at runtime
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.PROCESS_OUTGOING_CALLS, Manifest.permission.READ_PHONE_STATE},
1);
}
Add OutgoingCallReceiver in AndroidManifest file
<receiver
android:name=".application.services.OutgoingCallReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
This code will work fine with you, but when you need to upload your application on Google play, It is ok with NEW_OUTGOING_CALL and READ_PHONE_STATE permission but,
you will receive a policy notice from playStore as:
Your app manifest requests the Call Log permission group (e.g. PROCESS_OUTGOING_CALLS)
It must be actively registered as the default Phone or Assistant handler on the device.
in this case you have 2 solution only if you want to read OutCommingCall Number:
Send declaration form to google declaration form
Or Make your application dialer app
Check Developer Policy Center

From the documentation for android.intent.action.NEW_OUTGOING_CALL:
This constant was deprecated in API level 29. Apps that redirect
outgoing calls should use the CallRedirectionService API. Apps that
perform call screening should use the CallScreeningService API.
https://developer.android.com/reference/android/content/Intent
So I would implement this API first and check if it works as expected.

Answered in Kotlin, not Java:
From sdk >=29 (Android 10 and up) you can register your app as a CallRedirectionService, "to interact between Telecom and its implementor for making outgoing call with optional redirection/cancellation purposes."
This removes the need to create a custom BroadcastReceiver.
1. On your AndroidManifest.xml file:
<service
android:name=".MyCallRedirectionService"
android:exported="true"
android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallRedirectionService" />
</intent-filter>
</service>
2. Create MyCallRedirectionService:
class MyCallRedirectionService : CallRedirectionService() {
override fun onPlaceCall(
handle: Uri,
initialPhoneAccount: PhoneAccountHandle,
allowInteractiveResponse: Boolean
) {
// We can get the outgoing number from the handle parameter:
Log.i("Phone Number:", handle.toString())
}
}
3. Use the RoleManager class to prompt the user to select your app as their CallRedirectionService:
In this case, I'm requesting as soon as the app is created, over on the MainActivity onCreate() method:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (!isRedirection())
roleAcquire(RoleManager.ROLE_CALL_REDIRECTION)
}
Here are the used functions:
private fun isRedirection(): Boolean {
return isRoleHeldByApp(RoleManager.ROLE_CALL_REDIRECTION)
}
private fun isRoleHeldByApp(roleName: String): Boolean {
val roleManager: RoleManager? = getSystemService(RoleManager::class.java)
return roleManager!!.isRoleHeld(roleName)
}
private fun roleAcquire(roleName: String) {
val roleManager: RoleManager?
if (roleAvailable(roleName)) {
roleManager = getSystemService(RoleManager::class.java)
val intent = roleManager.createRequestRoleIntent(roleName)
startActivityForResult(intent, 1)
} else {
Toast.makeText(
this,
"Redirection call with role in not available",
Toast.LENGTH_SHORT
).show()
}
}
private fun roleAvailable(roleName: String): Boolean {
val roleManager: RoleManager? = getSystemService(RoleManager::class.java)
return roleManager!!.isRoleAvailable(roleName)
}

Related

How to solve- Onesignal push notification not coming to Android device if the app is not opened for a while?

In my Android application, I am using One Signal push notification service to send push notifications. I have done all the settings according to document as mentioned.
After setting up all these things, I have created one service class of one signal like below-
NotificationExtenderBareBonesExample.java
public class NotificationExtenderBareBonesExample extends NotificationExtenderService {
public static String first_screen;
#Override
protected boolean onNotificationProcessing(OSNotificationReceivedResult receivedResult) {
SharedPreferences.Editor editor = getApplicationContext().getSharedPreferences("NOTIFY_PREF", MODE_PRIVATE).edit();
editor.putString("notify_msg", receivedResult.payload.groupMessage);
editor.putString("notify_key", receivedResult.payload.groupKey);
editor.apply();
first_screen = receivedResult.payload.groupMessage;
return false;
}
}
I have also created another class to handle the received push notification like below-
ExampleNotificationReceivedHandler.java
public class ExampleNotificationReceivedHandler implements OneSignal.NotificationReceivedHandler {
#Override
public void notificationReceived(OSNotification notification) {
JSONObject data = notification.payload.additionalData;
String customKey;
if (data != null) {
customKey = data.optString("customkey", null);
if (customKey != null)
Log.i("OneSignalExample", "customkey set with value: " + customKey);
}
}
}
Then, in my Activity class, I have initialized One Signal like below-
OneSignal.startInit(this)
.inFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification)
.unsubscribeWhenNotificationsAreDisabled(true)
.setNotificationReceivedHandler(new ExampleNotificationReceivedHandler())
.setNotificationOpenedHandler(new MyNotificationOpenedHandler(this))
.init();
At last in my AndroidManifest file, I have declared the service like below-
<service
android:name="com.rokomari.new_package.notification_check.NotificationExtenderBareBonesExample"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE">
<intent-filter>
<action android:name="com.onesignal.NotificationExtender" />
</intent-filter>
</service>
The push notification was coming, if the app is being used recently, but the problem was still there as it is mentioned in my question. So, I have checked few more solutions and applied in my project like below-
I made my application a system app and added one authentication service with that.
I also added one Broadcast-Receiver class like below-
BootReceiver.java
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
Intent startServiceIntent = new Intent(context, NotificationExtenderBareBonesExample.class);
context.startService(startServiceIntent);
Intent notificationServiceIntent = new Intent(context, NotificationExtenderBareBonesExample.class);
context.startService(notificationServiceIntent);
}
}
}
And declared this in my AndroidManifest file-
<receiver
android:name="com.rokomari.new_package.notification_check.BootReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
After doing all these I am still having the same problem.
---- The Problem is -----
When I am using app(opening the app) regaularly the push notification from one signal is coming whether the app is in background or not. But when I am not using the app for a while it's not coming. In addition, for some devices mainly Xiomi the push notification is not coming at all.
After making some changes in Xiomi device like below-
*** Xiaomi - Make sure "Auto-start" property enabled for your app in the settings.**
After changing settings, the push notification came once. But I need a solution programmatically to send push notification to cover all the devices. If it is not possible yet with One Signal, then I like to know how apps like facebook, whatsapp send push notification?

Telephonymanager.EXTRA_INCOMING_NUMBER is deprecated in API level 29

I am in situation where I need to identify incoming call phone number in Android but when using TelephonyManager.EXTRA_INCOMING_NUMBER android studio warning EXTRA_INCOMING_NUMBER is deprecated.I gone through the developers.android.com, it shows apps performing call screening should use the CallScreeningService API instead. But I can't figure out how to use CallScreeningService to get incoming call phone number. Anyone can help me?
As #Saurabh said, the new way to screen calls is through the CallScreeningService. However, for the service to work on Android Q and up, the user needs to set your app as the default caller ID & spam app (which is done by using the new RoleManager class)
Register your screening service:
<service android:name="com.example.ScreeningService"
android:permission="android.permission.BIND_SCREENING_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallScreeningService"/>
</intent-filter>
</service>
Create you service class:
#RequiresApi(api = Build.VERSION_CODES.N)
class ScreeningService : CallScreeningService() {
override fun onScreenCall(details: Details) {
//code here
}
}
Request the screening role from the user in your main activity (or where ever you see as fit):
#RequiresApi(Build.VERSION_CODES.Q)
private fun requestScreeningRole(){
val roleManager = getSystemService(Context.ROLE_SERVICE) as RoleManager
val isHeld = roleManager.isRoleHeld(RoleManager.ROLE_CALL_SCREENING)
if(!isHeld){
//ask the user to set your app as the default screening app
val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING)
startActivityForResult(intent, 123)
} else {
//you are already the default screening app!
}
}
Catch the user's response:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
123 -> {
if (resultCode == Activity.RESULT_OK) {
//The user set you as the default screening app!
} else {
//the user didn't set you as the default screening app...
}
}
else -> {}
}
}
Apologies for using a hard coded request code >.<
Create a CallScreeningService like this:
class ScreeningService : CallScreeningService() {
override fun onScreenCall(callDetails: Call.Details) {
val phoneNumber = callDetails.handle.schemeSpecificPart
// Do stuff with phone number
}
}
And register this service in your AndroidManifest.xml:
<service android:name="your.package.ScreeningService"
android:permission="android.permission.BIND_SCREENING_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallScreeningService"/>
</intent-filter>
</service>
I know it's pretty late but if someone has similar problem I found a solution that works on API 28+ for TelephonyManager. For just identifying phone number CallScreeningService is an overkill, and you could probably override users private settings or another app that blocks spam calls.
You need to add android.permission.READ_CALL_LOG in your manifest and request it in a runtime.
if (Build.VERSION.SdkInt >= BuildVersionCodes.P) {
if (ApplicationContext.CheckSelfPermission(Manifest.Permission.ReadCallLog) != Permission.Granted)
{
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.ReadCallLog }, ACTION_READ_CALL_LOG);
}
}
Provided user allows your app to read call log, after broad cast receiver hits OnReceive => ActionPhoneStateChanged phone number will be empty at the first instance, but second time it should be populated.
So be prepared that on Api 28+ phone number could be identified at the second time.

List<UsageStats> return empty

I want to get diary use for all apps installed in phone, but always an empty list is returned. Below is the code i am using.
Here app.js from react-native call apps information from native code java
loadApps = async () => {
await ReturnAppsInformations.getApps()
.then(response => {
console.log(response); // only [] <----
this.setState({ apps });
})
.catch(error => {
console.warn(error);
});
}
Here is my simple native code to return array with data
UsageStatsManager manager = (UsageStatsManager) this.reactContext.getSystemService(this.reactContext.USAGE_STATS_SERVICE);
List<UsageStats> stats =manager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,1729196, System.currentTimeMillis());
WritableArray list2 = Arguments.createArray();
for (int i = 0; i < stats.size(); i++) {
UsageStats usageStats = stats.get(i);
WritableMap appInfo2 = Arguments.createMap();
appInfo2.putString("packageName", usageStats.getPackageName());
appInfo2.putDouble("firsTimeStamp", usageStats.getFirstTimeStamp());
appInfo2.putDouble("getTotalTimeInForeground",
usageStats.getTotalTimeInForeground());
list2.pushMap(appInfo2);
}
promise.resolve(list2);
What am I doing wrong?
This is my first app so I do not have much knowledge
Updated as suggested by Julien, but still results in a empty array.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.returnappsinformations"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
</manifest>
Ok, i found those gits about it...
https://github.com/mvincent7891/UsageStatsModule
https://github.com/shimatai/react-native-android-datausage
https://github.com/lucasferreira/react-native-android-permissions
Tonight im gonna to read them... But i belive, that resolve the problem to me!! Thanks for support!!!
Have you asked and set the necessary permissions to use UsageStatsManager?
UsageStatsManager's documentation states that you need to declare android.permission.PACKAGE_USAGE_STATS in your Manifest and that it needs to be turned on in the Settings of your phone for your particular application.
EDIT:
Permission to add in your Manifest:
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
That special permission can't be granted through the normal permissions API of Android. So you'll need to redirect your users to the Settings page where they can grant it manually. You can open the right Settings screen via the Intent:
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
you need to declare android.permission.PACKAGE_USAGE_STATS in your Manifest
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
as well as check at run time
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.PACKAGE_USAGE_STATS)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
}
for more check How to use UsageStatsManager?
As #JulienArzul pointed you need to add permission android.settings.USAGE_ACCESS_SETTINGS and then need to check whether you have it or not using below code:-
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(OPSTR_GET_USAGE_STATS, myUid(), context.getPackageName());
return mode == MODE_ALLOWED;
If above condition returns false, redirect user to that permission's settings screen
Intent intent = new Intent(MainActivity.this, AppLockService.class);
intent.setAction(AppLockService.ACTION_START_FOREGROUND_SERVICE);
startService(intent);

Check if Download Manager downloaded the file

How can I check if file has been downloaded and run its installation? I have a code:
public void downloadUpdate(String url){
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("Downloading...");
request.setTitle("App Update");
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
String name = URLUtil.guessFileName(url, null, MimeTypeMap.getFileExtensionFromUrl(url));
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, name);
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
}
To check whether the download manager downloaded the file, you must implements your BroatcastReceiver.
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0));
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Cursor cursor = manager.query(query);
if (cursor.moveToFirst()) {
if (cursor.getCount() > 0) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
String file = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
// So something here on success
} else {
int message = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON));
// So something here on failed.
}
}
}
}
}
However, I am not sure whether you can install the APK programmatically. For security reason, I don't think you can. For application update, I think you should use google versioning control. When you re-deploy your app using different version number, the users should able to update automatically (unless user turn off at google play). Hope that this will help.
Update
You do not need to call the method that I mention. You just need to declare your broadcast receiver at your manifest xml file and DownloadManager will call at when download complete. The xml look something like below:
<receiver
android:name=".BroadcastReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
<action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED" />
</intent-filter>
</receiver>
This is relatively an easy method. It worked for me:
You need to add the <receiver> tag in the manifest file as follows:
<application>
<receiver
android:name= "com.example.checkDownloadComplete" <!-- add desired full name here -->
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
</application>
This will register a Broadcast Receiver for an event where your download is completed. This will call the onReceive() method in your class as soon as download completes. Remember that you need to extend the BroadcastReceiver class, but not implement it. I declared a boolean variable as a toggle to check completion of download.
Hence, your Java class will be something like:
public static class checkDownloadComplete extends BroadcastReceiver{
public static boolean isDownloadComplete= false;
#Override
public void onReceive(Context context, Intent intent) {
isDownloadComplete = true;
Log.i("Download completed?", String.valueOf(isDownloadComplete));
}
}
To wait until or check whether your download has completed from any other class, use the following simple code in the desired appropriate place:
while(!checkDownloadComplete.isDownloadComplete){
// add necessary code to be executed before completion of download
}
//code after completion of download
But remember that if you need to check it multiple times in your project, then you'll need to reset the value of isDownloadComplete beforehand.

Check if IntentService started on boot is still running

Into manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Into application:
<service android:name="Myservice"/>
<receiver android:name="com.myapp.Onstart"> <!-- Tested also only .Onstart -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Onstart.java:
package com.myapp;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class Onstart extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")){
context.startService(new Intent(context, Myservice.class));
}
}
}
Myservice.java:
package com.myapp;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class Myservice extends IntentService
{
public Myservice() {
super("Myservice");
}
#Override
protected void onHandleIntent(Intent intent) {
int n=0;
while(true)
{
if (i==20) {
stopSelf();
}
i = i++;
Log.i("Test", "n."+n++);
try {
Thread.sleep(10000);
}
catch (InterruptedException e)
{ }
}
}
}
Launching the application manually is shown the Main.java, and I want to know there (in Main.java) if my IntentService Myservice is still running. How to do it?
While I know that your question is to whether it's running or not, I do not see why would you need to know. Since IntentService works on demand.
IntentService is a base class for Services that handle asynchronous
requests (expressed as Intents) on demand. Clients send requests
through startService(Intent) calls; the service is started as needed,
handles each Intent in turn using a worker thread, and stops itself
when it runs out of work.
Also, from Context.startService(Intent) call doc:
If the service is being started or is already running, the
ComponentName of the actual service that was started is returned; else
if the service does not exist null is returned.
If you must, you can check the startService(Intent) return parameter.
Edit: It seems that you need an started service handled solely by you, not the system. This will allow you to have your own stop condition. Please refer to Services and Services Guide on how to use them.
References:
IntentService
startService(Intent service)
Services
Services Guide

Categories

Resources