API Level 29 - MediaProjection is always requesting permission - android

until Android API28 when requesting Media Projection Services android allowed the user to grant access just once by selecting "Don't show again" check-box. So that everytime app wants to use media projection services the access was already granted.
In the android AP29 the dialog changed and this option does not exist anymore.
What can be done in order to the user just grant the access once to the app to use media projection? Thank you.

Well the problem was that I was calling
startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
every time, which is not necessary (createScreenCaptureIntent() leads to the dialog window which requests user interaction) My solution makes the dialog appear only once (if application was closed it will ask for permission one time again). All I had to do was making addiotional private static variables of type Intent and int.
private static Intent staticIntentData;
private static int staticResultCode;
On Activity result I assign those variables with the passed result code and intent:
if(staticResultCode == 0 && staticIntentData == null) {
sMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
staticIntentData = data;
staticResultCode = resultCode;
} else {
sMediaProjection = mProjectionManager.getMediaProjection(staticResultCode, staticIntentData)};
}
Every time I call my startprojection method, I will check if they are null:
if(staticIntentData == null)
startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
else
captureScreen();
If null it will request permission, if not it will start the projection with the static intent data and static int resultcode, so it is not needed to ask for that permission again, just reuse what you get in activity result.
sMediaProjection = mProjectionManager.getMediaProjection(staticResultCode, staticIntentData);
Simple as that! Now it will only showing one single time each time you use the app. I guess thats what Google wants, because there no keep decision checkbox in that dialog like in previous android versions

Related

Start Activity for Result Request Code in Library Project

I am currently working on a library project for Android which I plan on open sourcing. The library has an activity that I need to return a result for so the app that's utilising the library will need to call startActivityForResult.
My question is, if the user has an activity within their app, which calls a second activity within their own app which also needs to return a result, and this activity needs to call the activity from my library, my libraries activity, and their own activity will be using the same onActivityResult callback. Is there a way to avoid my activities request code, not conflicting with one of their own request codes, is it just a case of assume their own request codes are 1, 2, 3 etc and I start my libraries activity request code from some arbitrary number like 1000.
Is this just the way it works or is there a better way to avoid my request code for my library conflicting with another apps activities request code?
I think the library should give a parameter to specify request code by developer themselves for startActivityForResult, so they could never be conflicted in the same activity or fragment they have been called from.
Activity A can choose its own request codes, and Activity B will never know which request code is used by A.
Which is no problem because request codes are purely local. Each Activity instance is separate from other Activity instances - they won't be mixed up just because they all implement the same method (like onCreate() or in your case onActivityResult() ).
Let's take a look at some lines from the source code for android.app.Activity, starting at line 4614
public void startActivityForResult(#RequiresPermission Intent intent, int requestCode,
#Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
The comments in the quoted code snippet show that the request code is used to determine if there is a result to be returned.
Please note that the parameters for startActivityForResult(Intent intent, int requestCode, Bundle options) are passed into a method execStartActivity() from the Instrumentation class in the same package android.app.
But there are four other parameters to execStartActivity() which serve to identify the calling app and the current Activity instance (I won't go into details of the Binder framework here, but there is for example a youtube video on this topic):
Context who, IBinder contextThread, IBinder token, Activity target
Again, the request code is only used to determine if there is a result to be returned (for a negative request code, startActivityForResult() is handled just like startActivity())
return requestCode >= 0 ? am.getResult() : null;
Besides that, the request code is just passed back to the Activity which called startActivityForResult().
So if an Activity doesn't use the same request code for different types of requests, all is good.

On Android 8.1&Pixel 2, how to draw on top and check this permission?

Background
I've noticed that ever since Android O came, the function of Settings.canDrawOverlays had issues telling us if the app was granted with the draw-on-top permission (AKA "Display over other apps"), so I've reported about this:
https://issuetracker.google.com/issues/62047810
https://issuetracker.google.com/issues/68465333
The problem
Not sure why they were marked as fixed, but now that I've tested on my Pixel 2 with Android 8.1, I've noticed this function still ALWAYS returns false (so I've reported about this here) .
Thing is, I've tried finding an alternative and saw others also having this issue here on StackOverflow:
Settings.canDrawOverlays is returning false even after turning the permission on from settings
Settings.canDrawOverlays(context) returns false on Android Oreo
Why in Android O method Settings.canDrawOverlays() returns "false" when user has granted permission to draw overlays and returns to my application?
I don't know if this is an issue with Android 8.1, or just my Pixel 2, but I'm using the latest, stock rom version of it (OPM171019.013) .
What I've tried
I've tried all of the solutions that were suggested on the above links.
All of them return me the result that the permission is not granted, ever.
The only solution I've found that does something is this one, which just tells me that the permission was toggled.
One way to "solve" this, is to assume the permission is granted in case the app was installed via the Play Store, because it's granted by default when installing from there.
But this is not a good solution. Apps can be installed from outside of the Play Store, and the user can always turn off this permission manually.
EDIT: I don't know if that's even a possible "solution", because when I try on the app, I can't really draw on top, even if the permission is granted.
The questions
How can I check for sure if the current app is granted with this permission?
From which Android version does this issue occur? Or maybe it's an issue only for Pixel 2 ?
Seeing that even after granting the permission, the app fails to draw on top, is there a possible solution for it?
EDIT: Speaking with others, I think this is a very specific issue on Pixel 2 with Android 8.1 . The reason is that the issue doesn't seem to appear on other devices with Android 8.1. I hope it gets fixed.
I had the same issue, there is a problem with the function Settings.canDrawOverlays because that needs an app reboot to return true when you granted the permission, try these.
I have a button that calls a Config Activity to enable Overlay
public void setOverlayPermissions(){
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName()));
startActivityForResult(intent,app.REQUEST_ID_OVERLAY_PERMISSIONS);
}
After that, in onActivityResult funtion
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == app.REQUEST_ID_OVERLAY_PERMISSIONS){
new WaitingFor().execute();
}
}
And then
private class WaitingFor extends AsyncTask{
private ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setCancelable(false);
progressDialog.setMessage("Espera un momento...");
progressDialog.show();
}
#Override
protected Object doInBackground(Object[] objects) {
try{
Thread.sleep(2000);
}catch (Exception ex){
Log.e(TAG,ex.getMessage()+"");
}
return null;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
progressDialog.dismiss();
if(verifyWindowOverlay()){
FL.w(TAG,"Acepto Overlay");
setPaso2();
}else{
FL.w(TAG,"NO acepto Overlay [Permission error]");
Toast.makeText(MainActivity.this,"Falta configuracion, reintenta!", Toast.LENGTH_SHORT).show();
}
}
}
My verifyWindowsOverlay function
public boolean verifyWindowOverlay(){
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
return Settings.canDrawOverlays(this);
if(appOpsMgr.checkOpNoThrow(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, android.os.Process.myUid(), getPackageName()) == 0 || Settings.canDrawOverlays(this))
return true;
return false;
}
Good luck.

Working example of getting permission from a Service in Android Marshmallow?

I am aware about the changes introduced in Android 6.0/SDKVersion 23 regarding the run-time permission. There is already discussion and talks on this topic in the below post which talks about the various aspects of new permission model.
"How to request permissions from a Service in Android Marshmallow"
Google I/O 2015 - Android M Permissions
"Mother, May I?" Asking for Permissions (Android Dev Summit 2015)
After going thorough these article,I believe below is suggested to "workaround" (as Service doesnot have UI support)this problem.
Check permission checkSelfPermission() in the context of
Service.
Notify it to status bar in case the permission is denied.
User would now click on status bar which would launch a new
DialogActivity to request for permission when users press on the
notification.
I am not able to make the workable version of the suggestions provided to achieve this task. When we launch DialogActivity to ask for permission, it would be assigned and available for that activity. It would not be applicable for background service who had put it on status bar and even after this service would not be granted this permission(instead the permission would be given to DialogActivity).
Could somebody provide the input(possibly workable logic/code) so that I can make it workable(.i.e. execute the code in the context of Service which would be dependent on whether permission is granted or not).
My Scenario
Develop application which would send the SMS at regular interval
regarding the my current location.
I have designed the UI for this application as mentioned below. It had some settings parameters and also buttons which would start the main logic which is:
Start monitoring the current location and
Send the SMS to configured contact numbers.
I believe user would minimize this application( as it does not have any interesting logic apart from sending the SMS) and it should continue to work in background(.i.e. monitoring the location and sending the SMS). Hence I thought to use move this logic in Service instead of keeping it the Activity itself. If I keep these logic in Activity itself, it works as expected however for that user is suppose to keep the application in foreground all the time.
So far I have been able to achieve the following:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Define START/STOP button handles.
mStartApp = (ImageButton)findViewById(R.id.startApp);
mStopApp = (ImageButton)findViewById(R.id.stopApp);
//Write event handlers for above grabbed buttons.
mStartApp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent startService = new Intent(getApplicationContext(),
CurrentLocationTrackerService.class);
startService(startService);
}
});
mStopApp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent stopService = new Intent(getApplicationContext(),
CurrentLocationTrackerService.class);
stopService(stopService);
}
});
// Support for the ActionBar widgets menu in current activity.
Toolbar mToolBar = (Toolbar)findViewById(R.id.myToolbar);
if(mToolBar != null) {
setSupportActionBar(mToolBar);
}
}
//Other methods.....
}
public class CurrentLocationTrackerService extends Service {
public CurrentLocationTrackerService() { }
#Override
public void onCreate() { }
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int permissionLocationAccess = ContextCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.ACCESS_FINE_LOCATION);
// If permissionLocationAccess is true execute further
// If permissionLocationAccess false(which would always as we are in Service and it is dangerous
// permission) we need to put some information to status bar. Post that user would click on that
// which would launch some new activity where UI of asking permission would be shown. But still
// Service would not get the permission( only new activity would have that permission but it is
// of no use here as I am planning to put these logic iside the Service as my app may not be always
// be in foreground)
}
}
Hope I have provided all detail regarding the problem and also my application context regarding why I require to model in this way.The real point over here is how to achieve this.
I am really stuck at this point and any help would be highly appreciated. Kindly let me know in case anything else is required from my side.

How to detect sign out via the Google Play Leaderboards UI?

Signing out or disconnecting the GamesClient is straightforward when it is from your own UI, such as a button on the main menu.
However, users can also sign out from the game from the Google Play UI in the acheivements and leaderboard views displayed by the intents such as getAllLeaderboardsIntent(). (It's a bit hidden, but if you tap the menu in the upper right, it lets you sign out.)
There are a few promising listener interfaces like OnSignOutCompleteListener but they don't seem to work with a sign out via the google UI, only from your own UI calling GamesClient.signOut().
How can I detect that the user has signed out from the leaderboards or achievement intents? Is it possible to have a callback for this?
I want to be able to update my in-game UI to reflect the logged-in status.
Unfortunately GameHelper doesn't detect when you logout from Google play games.
What you need to do is to put this in your onActivityResult() method in your activity.
I encounter a crash error when I tried using aHelper.signOut() when res == RESULT_RECONNECT_REQUIRED is true.
Instead I created a resetAllValues() method which resets back all values to its default in GameHelper.
In my MainActivity.java
protected void onActivityResult(int req, int res, Intent data) {
super.onActivityResult(req, res, data);
if (res == GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED) {
aHelper.resetAllValues();
} else {
aHelper.onActivityResult(req, res, data);
}
}
My method in GameHelper.java
public void resetAllValues()
{
mProgressDialog = null;
mAutoSignIn = true;
mUserInitiatedSignIn = false;
mConnectionResult = null;
mSignInError = false;
mExpectingActivityResult = false;
mSignedIn = false;
mDebugLog = false;
}
Duplicate from:
How can i check if user sign's out from games services default view?
As I see it, there is no elegant solution to that. You can check the response_code in onActivityResult for INCONSISTENT_STATE and cut off the GamesClient, but I'm not sure, if you can potetially get to an inconsistent state in any other manner...

How to Make a Call directly in Android

I know it was very simple to do it but I come across a very strange issue. I have to call Police in danger Situation by just tapping a button. So I have used following code to call.
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:100"));
callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(callIntent);
Added CALL_PHONE permission in AndroidManifest.xml. The Issue is that it is opening the 100 on Dial Pad but not making call directly. I want that to happen immediately when user clicks on the button.
When I tried to to put +91 before 100 it is calling the number automatically but why plus is required for such numbers. So Someone help me how to solve this issue
From the documentation of ACTION_CALL:
Note: there will be restrictions on which applications can initiate a call; most applications should use the ACTION_DIAL.
Note: this Intent cannot be used to call emergency numbers. Applications can dial emergency numbers using ACTION_DIAL, however.
So it seems this behavior is on purpose.
There could be a problem that the android system doesnt recognize 100 as a valid phone number, instead if you put the country code before it then it works fine. TO solve such issue take a look at this library libnhonenumber. You could use it something like this
public static ArrayList<String> extractPhoneNumber(String content) {
ArrayList<String> numbers = new ArrayList<String>(0);
PhoneNumberUtil instance = PhoneNumberUtil.getInstance();
//Change IT with your contry code
Iterable<PhoneNumberMatch> matches = instance.findNumbers(content, "IT");
Iterator<PhoneNumberMatch> iterator = matches.iterator();
while (iterator.hasNext()) {
numbers.add(instance.format(iterator.next().number(), PhoneNumberFormat.INTERNATIONAL));
}
return numbers;
}
private void phoneCall()
{
String phoneCallUri = "tel:91";
Intent phoneCallIntent = new Intent(Intent.ACTION_CALL);
phoneCallIntent.setData(Uri.parse(phoneCallUri));
startActivity(phoneCallIntent);
}
Best way to directly call without user intervention..
String uri = "tel:" + num.trim();
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse(uri));
startActivity(intent);
There are two intents to call/start calling: ACTION_CALL and ACTION_DIAL.
ACTION_DIAL will only open the dialer with the number filled in, but allows the user to actually call or reject the call. ACTION_CALL will immediately call the number and requires an extra permission.
So make sure you have the permission
A Long time passed. But may help someone else.
If you want to call directly, you should use requestPermissions method.
1. Add this line to your manifest file:
<uses-permission android:name="android.permission.CALL_PHONE" />
2. Define a class variable in the activity class:
private static Intent phoneCallIntent; //If use don't need a member variable is good to use a static variable for memory performance.
3. Add these lines to the onCreate method of the activity:
final String permissionToCall = Manifest.permission.CALL_PHONE;
//Assume that you have a phone icon.
(findViewById(R.id.menuBarPhone)).setOnClickListener(new OnClickListener(){
public void onClick(View view) {
phoneCallIntent = new Intent(Intent.ACTION_CALL);
phoneCallIntent.setData(Uri.parse(getString(R.string.callNumber))); //Uri.parse("tel:your number")
if (ActivityCompat.checkSelfPermission(MainFrame.this, permissionToCall) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainFrame.this, new String[]{permissionToCall}, 1);
return;
}
startActivity(phoneCallIntent);
}
});
4. And for making a call immediately after clicking on Allow button, override onRequestPermissionsResult method:
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults){
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == 1){
final int permissionsLength = permissions.length;
for (int i = 0; i < permissionsLength; i++) {
if(grantResults[i] == PackageManager.PERMISSION_GRANTED){
startActivity(phoneCallIntent);
}
}
}
When a user give the permission, next time there will be no dialogue box and call will be make directly.

Categories

Resources