Telephonymanager.EXTRA_INCOMING_NUMBER is deprecated in API level 29 - java

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.

Related

Screen recording asks permission every time in android version 12 or above

Thanks for your attention about my trouble.
I am working with android + java & kotlin app.
It needs to be screen recorded but it asks permission every time in android version 12 or above.
Screen Record Permission Request
I hope this would ask only one time for permission request.
I've seen XRecorder does require once for screen recording permission not every time.
Isn't there anyway to make my app to ask only one time for permission request.
Wish someone would help me to solve this.
In my experience, you can save the result of media projection intent and reuse it during the same application process session. That is how most applications overcome this issue.
But be aware that it is more a hack than a solution because the docs do not guarantee it. In contrast, it says you can not reuse it. So use it at your own risk.
From my observations, it will work almost everywhere except some Xiaomi and Huawei devices. You can add them to ignore list and ask every time. Another good idea will be to add a try-catch when reusing an intent, so you can request permission again if the intent is expired.
Context: we use this hack in an application with millions of active users, so it has some credibility.
Code snippet to get the idea, but not production ready:
object MediaProjectionIntentHolder {
var intent: Intent? = null
}
class ScreenRecordingFragment : Fragment() {
// A user clicked button and this function is called
private fun startScreenRecording() {
val intent = MediaProjectionIntentHolder.intent
// You can check Huawei / Xiaomi devices here to ask permission every time
if (intent != null) {
recordScreen(intent)
} else {
requestPermission()
}
}
private fun recordScreen(intent: Intent) {
// Actual screen recording start
}
private showError() {
// show error
}
private fun requestPermission() {
val service = requireContext().getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
val intent = service.createScreenCaptureIntent()
val screenRecordingSupported = context.packageManager.queryIntentActivities(intent, 0).isNotEmpty()
if (screenRecordingSupported) {
startActivityForResult(intent, REQUEST_CODE)
} else {
showError()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
MediaProjectionIntentHolder.intent = data
recordScreen(data)
} else {
showError()
}
}
}
private companion object {
private consta val REQUEST_CODE = 42
}
}

How to detect outgoing call number in Android Q

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)
}

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);

How to exclude an Android App from Battery Optimization using code

I am new to Android, now I am working on a Project which is based on GPS. I got source code from internet(traccar). my requirement is like the app should update location on each 1Km or each 1hr. but the problem is the app not working in background after some time(10 - 20 mins). Is there any solution for this ?
what should I do(in code) to exclude this app from battery optimisation when the app is launching ? is it possible ?
I think you're having 2 different problems:
1) If you want to keep your app in background you should use a foreground Service. That way your app won't be considered to be in background by the system and the chances of its process being killed are reduced drastically. The downside is that as long as your Service is in foreground you need to show a permanent notification.
2) You cannot exclude your app from battery optimization yourself, but you can prompt the user the settings to whitelist your app. In order to do that you can refer to the official docs, you'll need to add the Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission to the manifest and then launch an intent with action ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS. The user will then be able to whitelist your app, only she/he can do that because otherwise every app would whitelist itself and the purpose of the battery optimization would be defied.
Add this permission in your manifest file Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
Request Permission at runtime inside onCreate method of your activity...
PowerManager powerManager = (PowerManager) getApplicationContext().getSystemService(POWER_SERVICE);
String packageName = "org.traccar.client";
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent i = new Intent();
if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
i.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
i.setData(Uri.parse("package:" + packageName));
startActivity(i);
}
}
This is the image of the code in debug mode:
This will be the view in app:
but the application not working as it is in No Restriction mode (background activity).
I want the application to work as No Restriction mode.
Add this permission in your manifest file
Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
Request Permission at runtime inside onCreate method of your activity...
private final int MY_PERMISSIONS_IGNORE_BATTERY_OPTIMIZATIONS =1;
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS),
MY_PERMISSIONS_IGNORE_BATTERY_OPTIMIZATIONS)
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
// Permission has already been granted
}
Here is the kotlin version of #Shafeeq Mohammed Answer and it worked for me. Thank you
fun checkBatteryOptimization(mContext: Context) {
val powerManager =
mContext.getSystemService(POWER_SERVICE) as PowerManager
val packageName = mContext.packageName
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val i = Intent()
if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
i.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
i.data = Uri.parse("package:$packageName")
mContext.startActivity(i)
}
}
}

Billing service unavailable on device. (response: 3:Billing Unavailable)

I've been struggling with this problem for days now. I know there are a lot of questions with the same problem on SO but i couldn't get it to work.
What I have done
Uploaded APK in beta phase
Created Merchant account
Added test user
Code
AndroidManifest.xml
<uses-permission android:name="com.android.vending.BILLING" />
MainActivity.java
public class MainActivity extends AppCompatActivity {
private IabHelper mHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ...
setupInAppBillings();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
// [....]
private void setupInAppBillings() {
String base64EncodedPublicKey = "MY PUBLIC KEY";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Toast.makeText(getContext(), "In-app Billing setup failed: " + result, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getContext(), "In-app Billing is set up OK", Toast.LENGTH_SHORT).show();
}
}
});
}
}
Tested on
Huawei P8 (Google Play Version 6.2.14)
In Switzerland, so a supported country for In-App Billing
What I've tried
Deleted cache and data from Google Play
Tutorial from Google Developer site
Went trough the checklist from user sokie: answer of sokie
The only thing I haven't done from this list is the setup of the License Verification Library (LVL). But I couldn't find any information that this step is required for an In-App Purchase. If not needed I want to do it without this library because I don't really need it as stated on the Google Site.
The Google Play Licensing service is primarily intended for paid applications that wish to verify that the current user did in fact pay for the application on Google Play.
Is there something I miss?
if you target android 31 you should add this to your manifest :
<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
</intent>
</queries>
Finally I got it to work! The problem was the following: Even though I put the IInAppBillingService.aidl in the com.android.vending.billing package, the generated class was in the wrong package as you can see in the code below.
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\path\\src\\main\\aidl\\com\\android\\vending\\billing\\IInAppBillingService.aidl
*/
package billing;
public interface IInAppBillingService extends android.os.IInterface { //... }
To solve this, I deleted and recreated the com.android.vending.billing package with the IInAppBillingService.aidl. So if you have the same problem, check twice where the IInAppBillingService.java was generated.
I recently faced this problem. As Bona Fide wrote, the package declaration in IInAppBillingService.aidl must be set to "com.android.vending.billing" and the aidl file should be found inside the corresponding directory using the explorer. Furthermore (and that was the problem in my case), in the IabHelper.java, the string parameter to serviceIntent must be the same as the package name that contains the IInAppBillingService.aidl file.
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");// correct package name: com.android.vending.billing
serviceIntent.setPackage("com.android.vending");
List<ResolveInfo> intentServices = mContext.getPackageManager().queryIntentServices(serviceIntent, 0);
if (intentServices != null && !intentServices.isEmpty()) {
// service available to handle that Intent
mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
}
else {
// no service available to handle that Intent
if (listener != null) {
listener.onIabSetupFinished(
new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE,
"Billing service unavailable on device."));
}
}
}

Categories

Resources