0

After reading the Android docs, I decided to have a play around and "read sms messages from my device."

However, after setting permissions in Manifest, and writing code to request permissions in code behind (java), and after setting a breakpoint on RequestPermission(), I can see that my app crashes because "Permission denied."

Why am I getting permission denied when I have declared everything the docs have told me to declare and have also implemented that thing where you get the OS to ask the user to grant or deny permission (which doesn't even happen - it doesn't present me with a dialog to grant or deny permissions - my app just crashes before it even shows up!)

How can I figure out what's going on? How can I ask the user for permission for [specifically] READ_SMS permission?

Note that I have been bashing my head against the desk while trying to fix this for days, so the code here is the latest code I have, however there have been about 20 (complete) re-writes. So if something is missing, it may have already been there but was removed because it didn't work, or was removed in order to troubleshoot to see what works what doesn't.

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.me.app">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_SMS" />
        <uses-permission android:name="android.permission.RECEIVE_SMS" />
        <uses-feature
            android:name="android.hardware.telephony"
            android:required="true">
        </uses-feature>
        <grant-uri-permission android:path="content://sms/"
            android:pathPattern=".*"
            android:pathPrefix="\"/>
        <activity
            android:name=".Main"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Main.java (onCreate(...)):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Testing Snackbar", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.setDrawerListener(toggle);
    toggle.syncState();

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);

    RequestPermission(this);
}

Main.java (permission methods):

private static final int MY_PERMISSIONS_REQUEST_READ_SMS = 226;

protected void RequestPermission(Activity activity)
{
    if(ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED)
    {
        if(ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.READ_SMS)) {
            return;
        }
        else
        {
            ActivityCompat.requestPermissions(activity, new String[] { Manifest.permission.READ_SMS
            }, MY_PERMISSIONS_REQUEST_READ_SMS);
        }
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
    switch(requestCode)
    {
        case MY_PERMISSIONS_REQUEST_READ_SMS:
        {
            if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
            {
                MessageAdapter messageAdapter = new MessageAdapter(GetMessages());
                RecyclerView recList = (RecyclerView) findViewById(R.id.messagesList);
                recList.setAdapter(messageAdapter);

                // CardView List
                LinearLayoutManager llm = new LinearLayoutManager(this.getApplicationContext());
                llm.setOrientation(LinearLayoutManager.VERTICAL);
                recList.setLayoutManager(llm);
            }
            else
            {
                return;
            }

            return;
        }
    }
}
username
  • 289
  • 4
  • 19

2 Answers2

2

One place you should fix is :

if(ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.READ_SMS)) {
            return;
        }

here you are simply returning, while you actually should show rationalle why you need this permission and then request it. The usual aproach is to use Snackbar or dialog or simply immediately call ActivityCompat.requestPermissions( - at least for testing purposes.

[edit]

  Snackbar.make(activity, R.string.rationaleStringId, Snackbar.LENGTH_INDEFINITE)
          .setAction(android.R.string.ok, new View.OnClickListener() {
            @Override
            @TargetApi(Build.VERSION_CODES.M)
            public void onClick(View v) {
                 ActivityCompat.requestPermissions(activity, new String[] {Manifest.permission.READ_SMS}, MY_PERMISSIONS_REQUEST_READ_SMS);
            }
          }).show();
marcinj
  • 48,511
  • 9
  • 79
  • 100
  • thank you. I just wrote what was written in the documentation. Had no idea I had to return rationale. Could you please give me a tinny little sample? I'm still a little new here (to Android), and a bit confused still. – username Dec 13 '15 at 09:42
  • What does `R.string.rationaleStringId` actually refer to? If I add it as a string in strings.xml it doesn't work. If I just leave it there, I'm not sure what to do with it. I already have a snackbar in my app and pass a string as an argument in that same place, and it works, but this code isn't working in the extension method (RequestPermission) – username Dec 13 '15 at 11:07
  • It is a string id, and actually should be lowercase, use explicit string IF it works for you – marcinj Dec 13 '15 at 11:24
  • Ah, @Marcin, it still isn't working. I think the problem lies elsewhere. When I'm debugging, I am getting an '12-13 23:03:12.181 8120-8120/com.me.app E/RecyclerView: No adapter attached; skipping layout' exception. I have no idea why. There are plenty of errors like this on Google but the solutions seem to be the same, and the code in my question as it is I believe should not be producing this error. – username Dec 13 '15 at 12:06
  • onRequestPermissionsResult is called asynchronously, so for some time - your recyclerview is shown with no adapter set. You should set adapter to it also in on create – marcinj Dec 13 '15 at 12:25
  • I'm not sure how I should set the adapter because the adapter requires data from the GetMessages() method, which only contains data that can be retrieved from the devices once permission has been granted. It kinda feels like a catch-22... Can't get permissions without setting adapter, but can't set adapter without permissions... Can I just set the adapter to null? I think I tried that without an luck. – username Dec 13 '15 at 12:30
1

Your <uses-permission> elements are in the wrong place, as is the <uses-feature> element. These go outside the <application> element, as direct children of the root <manifest> element.

Also, delete the <grant-uri-permission> element. Not only is it not in the correct place, but you don't have a correct place for it, as that only goes as a child of a <provider> element.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491