Auto read OTP/SMS in android - android

I am working on an Android App, in which server sends an OTP and the user needs to enter this OTP in the App, to SignUp for my App. What I want is, that my App should be able to automatically read the OTP sent by the server. How can I achieve this? Any help or guidance in this regard would be highly appreciated.
Thanks..! In advance

Using SmsVerifyCatcher library
In manifest , add these permissions
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
In build.gradle (app gradle)
implementation 'com.github.stfalcon:smsverifycatcher:0.3.2'
Initialize SmsVerifyCatcher in onCreate activity
smsVerifyCatcher = new SmsVerifyCatcher(getActivity(), new OnSmsCatchListener<String>() {
#Override
public void onSmsCatch(String message) {
String code = parseCode(message);//Parse verification code
Log.d("Agilanbu OTP", code);
Toast.makeText(getActivity(), "Agilanbu OTP: " + code, Toast.LENGTH_LONG).show();
et_otp.setText(code);//set code in edit text
}
});
In activity lifecycle
#Override
protected void onStart() {
super.onStart();
smsVerifyCatcher.onStart();
}
#Override
protected void onStop() {
super.onStop();
smsVerifyCatcher.onStop();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
smsVerifyCatcher.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
Parse the message
private String parseCode(String message) {
Pattern p = Pattern.compile("\\b\\d{6}\\b");
Matcher m = p.matcher(message);
String code = "";
while (m.find()) {
code = m.group(0);
}
return code;
}

Google Play doesn't allow RECIEVE_SMS permission anymore until and unless your app is default SMS handler.
So one possible solution as of now is to use SMS_RETRIEVE_API
you will need a BroadcastReceiver and a task that does SmsRetriever.getClient(context).startSmsRetriever();
In your receiver:
if(SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
final String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
}

You have 3 options to automatically read the OTP SMS:
1. Read all the incoming SMS using the SMS permission:
https://androidwave.com/automatic-sms-verification-android/
http://androidbymaitri.blogspot.in/2016/08/read-sms-automatically-to-verify-otp.html
Not advised anymore, as this requires the user to explicitly grant the SMS permission.
2. Using SMS Retriever API in Google play services:
https://developers.google.com/identity/sms-retriever/overview
https://www.youtube.com/watch?v=jzWYv8y2v1c
Advised. But this requires some server level changes in the OTP SMS format. And this works only in the devices that have Play services installed.
3. Using createAppSpecificSmsToken in the SmsManager class (from Android O only):
https://developer.android.com/reference/android/telephony/SmsManager.html#createAppSpecificSmsToken(android.app.PendingIntent
https://code.tutsplus.com/tutorials/android-o-phone-number-verification-with-sms-token--cms-29141
Not advised, because this works only in Android O, as of now.

Related

Permissions needed for NotificationManager

I'm trying to set the Ringer to Silent and Do not Disturb to Priority Only using the following
AudioManager myAudioMgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
NotificationManager myNOtificationMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
myAudioMgr.setRingerMode(AudioManager.RINGER_MODE_SILENT);
myNOtificationMgr.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
I keep getting a security error
java.lang.RuntimeException: Unable to start receiver MyBroadcastReceiver: java.lang.SecurityException: Notification policy access denied
I've added the Access Notification Policy permission to my Manifest file
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
Am I missing an additional permission?
It seems like the user needs to explicity grant the permission to app via the settings screen in order for the app to manipulate the priority/silent via the notifications api. I am assuming that you are using the notificationManager class for this. Some links that might help you:
https://developer.android.com/reference/android/provider/Settings.html#ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS
https://developer.android.com/reference/android/app/NotificationManager.html#ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED
I think essentially what you need to do is direct the user to the "Show Do Not Disturb access settings" and have him enable the option for notification management (Something similar for what would you do for mock location for example)
Sample code:
startActivityForResult(new Intent(android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS), 0);
Hope this helps.
From Android Developers:
Request policy access by sending the user to the activity that matches
the system intent action ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS.
Use ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED to listen for
user grant or denial of this access.
So, to get permission you should request policy access by sending the user to the activity that matches the system intent action ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS and listen for response using BroadcastReceiver with action ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED.
Also take a look at method isNotificationPolicyAccessGranted() from NotificationManager to check if permission granted.
You'll need to do something like
public class SomeFragment extends FragmentCompat {
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
int permissionNotifications = ContextCompat.checkSelfPermission(getActivity(), Manifest.permission. ACCESS_NOTIFICATION_POLICY);
if (permissionNotifications != PackageManager.PERMISSION_GRANTED ) {
ActivityCompat.requestPermissions(
getActivity(),
new String[] { Manifest.permission.ACCESS_NOTIFICATION_POLICY },
PERMISSION_REQUEST
);
}
}
}
keep in mind I'm not able to run this code so it may require some TLC but it's a start
To correct the java.lang.SecurityException: Access denied to process: 3454 error, android.permission.SET_WALLPAPER permission must be added to the Android Manifest.
Add the following line in your AndroidManifest.xml:
uses-permission android:name="android.permission.SET_WALLPAPER"

Android 6.0 - Bluetooth - No code exists for Action_Found broadcast intent

UPDATE
I tried many codes, also from examples shown on the internet. each of them follows my approach. After many hours of testing, i came to the conclusion that on Android 6.0 there's no chance to achieve bluetooth discovery of unknown devices, we can only retrieve the bonded ones.
I'm pretty sure there's something with this android version.
if someone knows how to fix this, i would really appreciate any help.
Original Post
My code is working fine, but no devices get found.
i only receive DISCOVERY_STARTED and DISCOVERY_FINISHED, so no devices are found, but using system app these devices get found.
This is the code of my application, hope it can help.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bluetoothAdapter= BluetoothAdapter.getDefaultAdapter();
//other stuff...
IntentFilter filter=new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(myreceiver,filter);
}
final BroadcastReceiver myreceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.i("test","RECEIVED: "+ action);
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
}
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
}
if(BluetoothDevice.ACTION_FOUND.equals(action))
{
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i("test", device.getName() + "\n" + device.getAddress());
}
}};
public void scanDevices(View v){
if (bluetoothAdapter.isEnabled()){
bluetoothAdapter.startDiscovery();
}
}
I already got permissions set:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Very simple solution:
1. Add FINE_LOCATION permission to manifest:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
2. Request FINE_LOCATION permission at runtime:
//since i was working with appcompat, i used ActivityCompat method, but this method can be called easily from Activity subclassess.
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},MY_PERMISSION_REQUEST_CONSTANT);
3. Implement onRequestPermissionsResult method:
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSION_REQUEST_CONSTANT: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//permission granted!
}
return;
}
}
}
All this because Marshmallows requires this permission in order to use bluetooth for discovery.
Since this permission belongs to the Dangerous group of permissions, simply declaring it in the manifest doesn't work, we need the user's explicit consent to use the position (even if we don't need the position actually).

Broadcast Receivers not working in Android 6.0 Marshmallow

I just updated my Nexus 5 to android 6, until now my app was working fine, but now the broadcast receivers are not working. Has something changed in the new version?
Here is the code I tried that was working on previous versions but not in marshmallow -
Android Manifest
<intent-filter >
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
<uses-permission android:name="android.permission.READ_SMS" ></uses-permission>
Broadcast Receiver
public String TAG ="someClass";
private static String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equalsIgnoreCase(ACTION_SMS_RECEIVED)) {
Log.d(TAG, "Received...");
}
}
Service
Broadcast_receiver broadcast_receiver = new Broadcast_receiver();
IntentFilter filter1 = new IntentFilter();
filter1.addAction("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(broadcast_receiver, filter1);
Similarly the broadcast receiver for PHONE_STATE is also not working.
Your app's target API level is 23, that is android M (6.0). In android M there are huge changes related to user-permissions.
Here is nice article explaining the changes.
As stated in Android - Requesting Permissions
Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app... The user can revoke the permissions at any time...
It's also stated that:
System permissions are divided into two categories, normal and dangerous:
Normal permissions do not directly risk the user's privacy. If your app lists a normal permission in its manifest, the system grants the permission automatically
Dangerous permissions can give the app access to the user's
confidential data. If you list
a dangerous permission, the user has to explicitly give approval to
your app
Here are full lists of Dangerous Permissions and Normal Permissions
All that basically means that you need to manually request for any dangerous permission, when it's actually needed.
Since it potentially might be needed multiple times in your code, you can create a reusable method that checks whether specific permission is granted already and if it's not - to request it from user.
Here an example:
Java
public class PermissionManager {
//A method that can be called from any Activity, to check for specific permission
public static void check(Activity activity, String permission, int requestCode){
//If requested permission isn't Granted yet
if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
//Request permission from user
ActivityCompat.requestPermissions(activity,new String[]{permission},requestCode);
}
}
}
Kotlin
object PermissionManager {
//A method that can be called from any Activity, to check for specific permission
fun check(activity: Activity, permission: String, requestCode: Int) {
//If requested permission isn't Granted yet
if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
//Request permission from user
ActivityCompat.requestPermissions(activity, arrayOf(permission), requestCode)
}
}
}
Usage:
Java
//Inside your activity:
//1. Define static constant for each permission request
public static final int REQUEST_CODE_FOR_SMS=1;
//2. When needed (for example inside .onStart event) use method PermissionManager.check for requested permission
#Override
protected void onStart() {
super.onStart();
PermissionManager.check(this, Manifest.permission.RECEIVE_SMS, REQUEST_CODE_FOR_SMS);
}
//3. Handle User's response for your permission request
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if(requestCode==REQUEST_CODE_FOR_SMS){//response for SMS permission request
if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
//What to do if User allowed SMS permission
}else{
//What to do if user disallowed requested SMS permission
}
}
}
Kotlin
//Inside your activity:
//1. Define static constant for each permission request
val REQUEST_CODE_FOR_SMS = 1
//2. When needed (for example inside .onStart event) use method PermissionManager.check for requested permission
override fun onStart() {
super.onStart()
PermissionManager.check(this, Manifest.permission.RECEIVE_SMS, REQUEST_CODE_FOR_SMS)
}
//3. Handle User's response for your permission request
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (requestCode == REQUEST_CODE_FOR_SMS) {//response for SMS permission request
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//What to do if User allowed SMS permission
} else {
//What to do if user disallowed requested SMS permission
}
}
}
Note:
If you need to use PermissionManager.check inside Fragment instance, use: getActivity() as its first parameter.
You can use checkSelfPermission inside Service instance, to check if some permission is granted already, but not requestPermissions to request it. Because checkSelfPermission can be used for any Context, but requestPermissions only for Activity
Marshmallow is blocking the dangerous permissions.
This doesn't apply to the scenario listed, but it might help someone else. I kept coming to this SO for why some of our Broadcast Receiver's weren't working. We have a custom permission setup and had the android:protectionLevel="dangerous". Changed it to android:protectionLevel= "signature"and everything started working.

Android 6 bluetooth

I upgraded to Android 6 and my applications who use Bluetooth doesn't work with this new API version. It's the same problem with application on Play Store: Bluetooth spp tools pro (good application to view if bluetooth works) which doesn't discovery of devices.
The problem seems to be in Bluetooth discovery:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.startDiscovery()
Log.i("BLUETOOTH", String.valueOf(mBluetoothAdapter.isDiscovering())); // Return false
My applications work well with Android 4/5 and I followed : http://developer.android.com/guide/topics/connectivity/bluetooth.html
Staring with Android 6.0 it is not enough to include permissions on manifest.
You have to ask the user explicitly about each permission that is considered "dangerous".
BluetoothDevice.ACTION_FOUND requires BLUETOOTH and ACCESS_COARSE_LOCATION permissions
http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#ACTION_FOUND
The ACCESS_COARSE_LOCATION
http://developer.android.com/reference/android/Manifest.permission.html#ACCESS_COARSE_LOCATION
is a "dangerous" permission and therefore you have to ask for it using requestPermission before doing actual discovery.
public void doDiscovery() {
int hasPermission = ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION);
if (hasPermission == PackageManager.PERMISSION_GRANTED) {
continueDoDiscovery();
return;
}
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{
android.Manifest.permission.ACCESS_COARSE_LOCATION},
REQUEST_COARSE_LOCATION_PERMISSIONS);
}
then on you will get the user answer on onRequestPermissionsResult
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_COARSE_LOCATION_PERMISSIONS: {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
continueDoDiscovery();
} else {
Toast.makeText(this,
getResources().getString(R.string.permission_failure),
Toast.LENGTH_LONG).show();
cancelOperation();
}
return;
}
}
}
To work with previous versions of android you should use compatibility libraries and make the calls using ActivityCompat
I've spent some time investigating the problem.
Created bug report on Android bug tracker here
The problem is that system does not forward BluetoothDevice.ACTION_FOUND intents to the registered BroadcastReceiver. Logcat shows lines like this:
10-16 07:34:09.147 786-802/? W/BroadcastQueue﹕ Permission Denial: receiving Intent { act=android.bluetooth.device.action.FOUND flg=0x10 (has extras) } to ProcessRecord{5ce2d92 21736:com.example.mvl.bluetoothtest/u0a74} (pid=21736, uid=10074) requires android.permission.ACCESS_COARSE_LOCATION due to sender com.android.bluetooth (uid 1002)
Which themes for me that the application needs android.permission.ACCESS_COARSE_LOCATION permission to receive this intents. i personaly don't understand why I need that permission to get the Bluetooth devices around.
So if you add this permission to you Manifest, then it should work with one more precondition - You have to set target SDK and compile with SDK not higher, then 22.
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
When checking the source code in GattService.java,you will find some code comments in method onScanResult:
// Do no report if location mode is OFF or the client has no location permission
// PEERS_MAC_ADDRESS permission holders always get results
if (hasScanResultPermission(client) && matchesFilters(client, result)) {
try {
ScanSettings settings = client.settings;
if ((settings.getCallbackType() &
ScanSettings.CALLBACK_TYPE_ALL_MATCHES) != 0) {
app.callback.onScanResult(result);
}
} catch (RemoteException e) {
Log.e(TAG, "Exception: " + e);
mClientMap.remove(client.clientIf);
mScanManager.stopScan(client);
}
}
this clarified what is needed to get a Bluetooth LE advertising report.

Lollipop : Accept incoming call Programatically in android lollipop

I'm using the below code to answer an incoming call from my app(BroadcastReceiver's onReceive()) , it is working in Kitkat . The same code is not working in Lollipop.
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
// send ordered broadcast
context.sendOrderedBroadcast(intent, null);
Please let me know how can I answer a call in Lollipop.
Thank you.
This worked for me.Put this code in your broadcast reciever for action "android.intent.action.PHONE_STATE" .Your phone needs to be rooted.Generate an apk file of ur app and put it into /system/priv-apps/ .Works for Android v 5.0 i.e lollipop.
final String LOG_TAG = "TelephonyAnswer";
TelephonyManager tm = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
try {
if (tm == null) {
// this will be easier for debugging later on
throw new NullPointerException("tm == null");
}
// do reflection magic
tm.getClass().getMethod("answerRingingCall").invoke(tm);
} catch (Exception e) {
}
Do not forget to add permission in ur manifest.
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
if modify_phone_state doesnt work explicitly ask for one by using this code
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.MODIFY_PHONE_STATE},
1);
and overide method
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case 1: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
} else {
Toast.makeText(MainActivity.this, "Permission denied to pick call", Toast.LENGTH_SHORT).show();
}
return;
}
}
}
From Lollipop, if our application is System app or application has root access then only we can programmatically answer incoming call
For 3rd party developer application, Lollipop OS do not allow to
answer programmatically to incoming call
You can check if how to do it for system app or with root access : In this Answer

Categories

Resources