49

My widget makes calls to secure permissions outside of an Activity scope. Is it possible to request permissions for Android M outside of an Activity?

N J
  • 27,217
  • 13
  • 76
  • 96
Dave Honly
  • 568
  • 1
  • 4
  • 5

6 Answers6

10

I found a workaround which seems to work fine. The trick is to create a transparent activity which is only there to request the permissions and is finished immediately afterwards. You'll still need a context of course but it doesn't have to be an activity. The activity can return the result (granted or denied) via a broadcast (since startActivtyForResult is not possible outside of an activity).

You can use this activity:

import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.support.v4.app.ActivityCompat
import android.support.v7.app.AppCompatActivity

internal const val PERMISSIONS_KEY = "permissions"
internal const val ACTION_PERMISSIONS_GRANTED = "GetPermissionsActivity.permissions_granted"
internal const val ACTION_PERMISSIONS_DENIED = "GetPermissionsActivity.permissions_denied"

class GetPermissionsActivity: AppCompatActivity() {

    private val permissionRequestCode = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ActivityCompat.requestPermissions(
            this,
            intent.getStringArrayExtra(PERMISSIONS_KEY),
            permissionRequestCode
        )
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        if (requestCode == permissionRequestCode) {
            if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                sendBroadcast(Intent(ACTION_PERMISSIONS_GRANTED))
            } else {
                sendBroadcast(Intent(ACTION_PERMISSIONS_DENIED))
            }
            finish()
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        }
    }
}

And this style for the activity

<style name="Theme.Transparent" parent="Theme.AppCompat">
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:backgroundDimEnabled">false</item>
    <item name="android:windowAnimationStyle">@null</item>
</style>

In the manifest:

<activity android:name="GetPermissionsActivity" android:theme="@style/Theme.Transparent" />

And then use it like this (context required)

class SomeClass : BroadcastReceiver() {

    private fun someFunction(context: Context) {
        val intentFilter = IntentFilter()
        intentFilter.addAction(ACTION_PERMISSIONS_GRANTED)
        intentFilter.addAction(ACTION_PERMISSIONS_DENIED)
        context.registerReceiver(this, intentFilter)
        val intent = Intent(context, GetPermissionsActivity::class.java)
        intent.putExtra(PERMISSIONS_KEY, arrayOf(<your permissions>))
        context.startActivity(intent)
    }

    override fun onReceive(context: Context, intent: Intent) {
        when {
            intent.action == ACTION_PERMISSIONS_GRANTED -> {
                context.unregisterReceiver(this)
                onPermissionsGranted()
            }
            intent.action == ACTION_PERMISSIONS_DENIED -> {
                context.unregisterReceiver(this)
                onPermissionsDenied()
            }
            else -> super.onReceive(context, intent)
        }
    }

    private fun onPermissionsGranted() {
        // ...
    }

    private fun onPermissionsDenied() {
        // ...
    }
}
5

No, it's not possible. What you can do is to send a notification where the user can tap and then use an activity to request/manage the permission (maybe with dialog theme).

greywolf82
  • 21,813
  • 18
  • 54
  • 108
  • 1
    The Configuration Activity is also a good place to deal with this. Though the user could go to the permissions screen and revoke any previously given permissions, so the notifcation-approach would still be needed. I'd also consider putting the widget into a permission-revoked UI state as an alternate approach. – Zsombor Erdődy-Nagy Sep 20 '15 at 19:27
4

You could use the Easy Permissions library.

Android requires that these request come from an Activity. With Easy Permissions this is no longer an issue, you may request permission from anywhere as long as you provide Context. In addition, if you request a permission that is already granted the user will not be prompted.

enter image description here

Full disclosure, our company manages and develops this free to use library. That being said, we are confident that it is a useful tool and we would not share it otherwise.

Newtron Labs
  • 809
  • 1
  • 5
  • 17
  • This doesn't make any sense to me. If Android really had the requirement to use an activity to request permissions your library had to be pure magic because it could do the impossible. The truth is that you use a context. That's the same like standard checkSelfPermission() does. I can't see the real advantage in using a 3rd party library for this permission check. Maybe I miss something... – The incredible Jan Mar 01 '18 at 15:16
  • @TheincredibleJan Thanks for reaching out. Android does require an `Activity`. `Context` is needed for the library, like many libraries do -- it is magic but still it is based in the real world :). The biggest advantage is that you can request to enable a permission from any place, even a class that is not an `Activity`. Also, the latest version will allow you to automatically enable all the permissions that are in your `Manifest` file. There is a sample app on the GitHub page; if you have a few minutes you should definitely check it out. Please let us know if you have any more questions. – Newtron Labs Mar 11 '18 at 04:03
  • 1
    this solution tested on S5 Android 6.0 and is not working, currently it does not doing nothing. – Matan Tubul Jan 02 '19 at 08:35
  • Unable to install the library – M. Usman Khan Dec 07 '22 at 08:36
2

You can only request permission either from Activity or from fragment.

Figure out a point in your Activity or Fragment where you feel App will require a permission, then call requestPermission method. sending notification will not work because you wanna your code processing un-till you get that requested permissions, and then resume your functionality from onRequestPermissionResult() method.

dex
  • 5,182
  • 1
  • 23
  • 41
2

I think it's possible to request a permission outside of an Activity, as long as you use the method

ActivityCompat.requestPermissions (Activity activity, String[] permissions, int requestCode)

from the support library and pass the Activity as a parameter of the method.

For example:

ActivityCompat.requestPermissions(targetActivity, new String[] {Manifest.permission.CAMERA}, PERMISSION_REQUEST_CODE);

where targetActivity is the Activity that should implement the method:

onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults)

That's the method that will handle de permission request result.

Fede
  • 117
  • 1
  • 2
  • 37
    Outside of Activity means from a Service or a BroadcastReceiver, where you don't have an Activity at all. This answer doesn't help at all. – Gabe Sechan Feb 07 '16 at 23:28
  • In general you can pass a context object to a non activity class and use it when a context is needed and you don't have an activity. Asking for permissions is a different story though. – Pedram Apr 16 '16 at 19:16
  • @Gabe on one side you are correct, but on the other side, the question doesn´t point out from where the questioner wants to call the request. So I think this is a correct answer. – Opiatefuchs May 20 '16 at 04:26
  • @GabeSechan how to set permissions in android M in case of services and broadcast ? – Jatin Khattar Aug 11 '16 at 11:45
  • how to set the permission if it is in the`AppWidgetProvider`?or should I put it in my `RemoteViewsService.RemoteViewsFactory`? My `ListView` in my widget needs to check the location. – natsumiyu Sep 22 '16 at 08:07
  • @AndyBoy As of right now, I know of no way. My best answer is to launch an activity to ask for it – Gabe Sechan Oct 24 '17 at 12:37
1

I was creating an app that required to check for permissions in many activities, so I created a static class that I could use globally in the app. And it worked. This worked for me.

I created a method to check request for permissions in a different class like this.

public class CheckForPermissions implements ActivityCompat.OnRequestPermissionsResultCallback {
private static final int MY_PERMISSIONS_REQUEST_READ_LOCATION = 1;


    public static void checkForLocationPermissions(Activity context){
        if(ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)  != PackageManager.PERMISSION_GRANTED
        || ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)  != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(context,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    MY_PERMISSIONS_REQUEST_READ_LOCATION);

    }
}}

In my Activity, I called the method like this

        CheckForPermissions.checkForLocationPermissions(this);
Enock Lubowa
  • 679
  • 8
  • 12