Android Nammu

| Page Views: 38

Android Nammu Permissions Library Tutorial and Example.

Nammu is a permission helper library for Android M, background check, monitoring and more.

Nammu doesn't just have a beautiful name but is also a great library, allowing us speed up our work with the Runtime Permissions introduced in Android 6.0 Marshmallow.

This is important as alot of developers are still confused about handling permissions since Android API 23.

Nammu was created by Michal Tajchert.

Nammu simplifies your work, allowing you to:

  1. Monitor permissions.
  2. Check them in background.
  3. Ask for a permission in easy way (callback).

Read more about permissions here.

Android Nammu

Monitoring permissions in Nammu

You can monitor a particular permission with Nammu in the following way.

  1. First init Nammu : Nammu.init(Context);.
    You will pass Application Context, not Activity Context.
  2. Secondly invoke permissionCompare(PermissionListener). This will compare lists of granted permissions with previous method call. If you want only to update granted permission lists (without checking if anything changed) use refreshMonitoredList(). PermissionListener will offer you a callback when permissions are changed, removed, or added. It is recommended to do on app start to check if user didn't removed any permissions and open our app again.

Also you can add Permission to ignored list to ignore its changes in access - use ignorePermission(String permission).

Permission Requesting in Nammu

Nammu allows you to request permissions in an easy manner.It removes a bit of boiler plate needed to maintain request id. Thus this simplifies our code inside our activity. To request for permissions here's what you do:

  1. Invoke Nammu.askForPermission(Activity, PermissionString , PermissionCallback).
  2. This then offers us a nice callback with either success or fail method. To use that callback teh only thing we need to add is in our Activity that we are using.
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    Nammu.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

List Permmissions in Nammu

Nammu also allows us get a list of monitored Permissions through Nammu.getPrevPermissions(). Also we can get a list of currently granted permissions through getGrantedPermissions().

Installing Nammu

Nammu is hosted in jitpack.io, so first we have to go to our project level build.gradle and register it's maven url. This is the build.gradle found inside the project folder of your app as opposed to the app folder.

repositories {
    maven {
        url "https://jitpack.io"
    }
}

Once you've done that then you go to the app level build.gradle and add the implementation statement for nammu:

dependencies {
    compile 'com.github.tajchert:nammu:1.2.0'
}

Full Runtime Permissions Example through Nammu

Let's look at a full example using Nammu.

(a). Add dependencies

First we need to add dependencies in our app level build.gradle:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':nammu')
    implementation "com.android.support:support-v4:27.1.1"
    implementation "com.android.support:support-v13:27.1.1"
    implementation "com.android.support:cardview-v7:27.1.1"
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
    implementation 'com.android.support:design:27.1.1'
}

Make sure in your project level build.gradle you have registered jitpack.io as a repository:

repositories {
    maven {
        url "https://jitpack.io"
    }
}
(b). AndroidManifest.xml

Go ahead and add the following permissions in your android manifest file.

    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.READ_CALENDAR"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.BODY_SENSORS"/>
    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>

    <!-- Note that all required permissions are declared here in the Android manifest.
     On Android M and above, use of these permissions is only requested at run time. -->
    <uses-permission android:name="android.permission.CAMERA"/>

    <!-- The following permissions are only requested if the device is on M or above.
     On older platforms these permissions are not requested and will not be available. -->
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
(c). activity_main.xml

Our activity_main.xml layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:id="@+id/main_layout"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/info_text"
        android:layout_margin="@dimen/margin_medium"
        android:textAppearance="?android:attr/textAppearanceMedium"/>

    <Button
        android:id="@+id/buttonContacts"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get Contacts" />

    <Button
        android:id="@+id/buttonLocation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get Location" />
    <Button
        android:id="@+id/buttonSpecialChangeSettings"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Allow to change settings" />

    <Button
        android:id="@+id/buttonStartFragmentActivity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start activity with fragment" />
</LinearLayout>
(d). activity_fragment.xml

Our activity_fragment.xml layout

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="pl.tajchert.nammusample.MyFragment" />
(e). fragment_my.xml

Our fragment_my.xml layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    >

    <Button
        android:id="@+id/buttonContacts"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get Contacts" />

    <Button
        android:id="@+id/buttonLocation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get Location" />

</LinearLayout>
(g). Tools.java

Our Tools.java file:

import android.content.Context;
import android.database.Cursor;
import android.location.Criteria;
import android.location.LocationManager;
import android.provider.ContactsContract;

public class Tools { 
    public static boolean accessContacts(Context context) {
        try { 
            Cursor cursor = context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
            if (cursor != null) {
                cursor.close();
            } 
        } catch (SecurityException e) {
            //No android.permission-group.CONTACTS 
            return false; 
        } 
        return true; 
    } 

    public static boolean accessLocation(Context context) {
        LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        Criteria criteria = new Criteria();
        criteria.setPowerRequirement(Criteria.POWER_LOW);
        String bestProvider = locationManager.getBestProvider(criteria, false);
        if (bestProvider == null) {
            //No android.permission-group.LOCATION 
            return false; 
        } 
        return true; 
    } 
} 
(h). MyFragment.java

Our MyFragment.java file:

import android.Manifest;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.Snackbar; 
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import butterknife.ButterKnife; 
import butterknife.OnClick; 
import pl.tajchert.nammu.Nammu; 
import pl.tajchert.nammu.PermissionCallback; 
import pl.tajchert.nammu.PermissionListener; 

public class MyFragment extends Fragment {

    /** 
     * Used to handle result of askForPermission for Contacts Permission, in better way than onRequestPermissionsResult() and handling with big switch statement 
     */ 
    final PermissionCallback permissionContactsCallback = new PermissionCallback() {
        @Override 
        public void permissionGranted() { 
            boolean hasAccess = Tools.accessContacts(getContext()); 
            Toast.makeText(getContext(), "Access granted = " + hasAccess, Toast.LENGTH_SHORT).show(); 
        } 

        @Override 
        public void permissionRefused() { 
            boolean hasAccess = Tools.accessContacts(getContext()); 
            Toast.makeText(getContext(), "Access granted = " + hasAccess, Toast.LENGTH_SHORT).show(); 
        } 
    }; 

    /** 
     * Used to handle result of askForPermission for Location, in better way than onRequestPermissionsResult() and handling with big switch statement 
     */ 
    final PermissionCallback permissionLocationCallback = new PermissionCallback() {
        @Override 
        public void permissionGranted() { 
            boolean hasAccess = Tools.accessLocation(getContext()); 
            Toast.makeText(getContext(), "Access granted = " + hasAccess, Toast.LENGTH_SHORT).show(); 
        } 

        @Override 
        public void permissionRefused() { 
            boolean hasAccess = Tools.accessLocation(getContext()); 
            Toast.makeText(getContext(), "Access granted = " + hasAccess, Toast.LENGTH_SHORT).show(); 
        } 
    }; 

    public MyFragment() { 
        // Required empty public constructor 
    } 

    public static MyFragment newInstance() { 
        MyFragment fragment = new MyFragment();
        return fragment;
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment 
        View view = inflater.inflate(R.layout.fragment_my, container, false);
        ButterKnife.bind(this, view);
        return view;
    } 

    @Override 
    public void onResume() { 
        super.onResume(); 
        Nammu.permissionCompare(new PermissionListener() { 
            @Override 
            public void permissionsChanged(String permissionRevoke) {
                //Toast is not needed as always either permissionsGranted() or permissionsRemoved() will be called 
                //Toast.makeText(MainActivity.this, "Access revoked = " + permissionRevoke, Toast.LENGTH_SHORT).show(); 
            } 

            @Override 
            public void permissionsGranted(String permissionGranted) {
                Toast.makeText(getContext(), "Access granted = " + permissionGranted, Toast.LENGTH_SHORT).show();
            } 

            @Override 
            public void permissionsRemoved(String permissionRemoved) {
                Toast.makeText(getContext(), "Access removed = " + permissionRemoved, Toast.LENGTH_SHORT).show();
            } 
        }); 
    } 

    @OnClick(R.id.buttonContacts)
    public void clickButtContacts() { 
        //Lets see if we can access Contacts 
        if(Nammu.checkPermission(Manifest.permission.READ_CONTACTS)) {
            //We have a permission, easy peasy 
            boolean hasAccess = Tools.accessContacts(getContext());
            Toast.makeText(getContext(), "Access granted = " + hasAccess, Toast.LENGTH_SHORT).show();
        } else { 
            //We do not own this permission 
            if (Nammu.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
                //User already refused to give us this permission or removed it 
                //Now he/she can mark "never ask again" (sic!) 
                Snackbar.make(getView(), "Here we explain user why we need to know his/her contacts.",
                        Snackbar.LENGTH_INDEFINITE) 
                        .setAction("OK", new View.OnClickListener() {
                            @Override 
                            public void onClick(View view) {
                                Nammu.askForPermission(MyFragment.this, Manifest.permission.READ_CONTACTS, permissionContactsCallback);
                            } 
                        }).show(); 
            } else { 
                //First time asking for permission 
                // or phone doesn't offer permission 
                // or user marked "never ask again" 
                Nammu.askForPermission(this, Manifest.permission.READ_CONTACTS, permissionContactsCallback);
            } 
        } 
    } 

    @OnClick(R.id.buttonLocation)
    public void clickButtLocation() { 
        if(Nammu.checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
            boolean hasAccess = Tools.accessLocation(getContext());
            Toast.makeText(getContext(), "Access granted fine= " + hasAccess, Toast.LENGTH_SHORT).show();
        } else { 
            if (Nammu.shouldShowRequestPermissionRationale(MyFragment.this, Manifest.permission.ACCESS_FINE_LOCATION)) {
                //User already refused to give us this permission or removed it 
                //Now he/she can mark "never ask again" (sic!) 
                Snackbar.make(getView(), "Here we explain user why we need to know his/her location.",
                        Snackbar.LENGTH_INDEFINITE) 
                        .setAction("OK", new View.OnClickListener() {
                            @Override 
                            public void onClick(View view) {
                                Nammu.askForPermission(MyFragment.this, Manifest.permission.ACCESS_FINE_LOCATION, permissionLocationCallback);
                            } 
                        }).show(); 
            } else { 
                //First time asking for permission 
                // or phone doesn't offer permission 
                // or user marked "never ask again" 
                Nammu.askForPermission(MyFragment.this, Manifest.permission.ACCESS_FINE_LOCATION, permissionLocationCallback);
            } 
        } 
    } 

    @Override 
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        Nammu.onRequestPermissionsResult(requestCode, permissions, grantResults);
    } 

} 
(e). FragmentActivity.java

Our FragmentActivity.java file:

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; 
import pl.tajchert.nammu.Nammu; 

public class FragmentActivity extends AppCompatActivity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment);
        Nammu.init(getApplicationContext()); 
    } 
} 
(f). MainActivity.java

Our MainActivity.java file:


import android.Manifest;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.Snackbar; 
import android.support.v7.app.AppCompatActivity; 
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import butterknife.BindView; 
import butterknife.ButterKnife; 
import butterknife.OnClick; 
import pl.tajchert.nammu.Nammu; 
import pl.tajchert.nammu.PermissionCallback; 
import pl.tajchert.nammu.PermissionListener; 

public class MainActivity extends AppCompatActivity { 
  private static final String TAG = MainActivity.class.getSimpleName();
  private static final int REQUEST_CODE_CONTACTS = 123;
  private static final int REQUEST_CODE_LOCATION = 124;
  private static final int REQUEST_CODE_BOTH = 125;

  @BindView(R.id.main_layout) View mLayout;

  /** 
   * Used to handle result of askForPermission for Contacts Permission, in better way than 
   * onRequestPermissionsResult() and handling with big switch statement 
   */ 
  final PermissionCallback permissionContactsCallback = new PermissionCallback() {
    @Override public void permissionGranted() { 
      boolean hasAccess = Tools.accessContacts(MainActivity.this); 
      Toast.makeText(MainActivity.this, "Access granted = " + hasAccess, Toast.LENGTH_SHORT).show(); 
    } 

    @Override public void permissionRefused() { 
      boolean hasAccess = Tools.accessContacts(MainActivity.this); 
      Toast.makeText(MainActivity.this, "Access granted = " + hasAccess, Toast.LENGTH_SHORT).show(); 
    } 
  }; 

  /** 
   * Used to handle result of askForPermission for Location, in better way than 
   * onRequestPermissionsResult() and handling with big switch statement 
   */ 
  final PermissionCallback permissionLocationCallback = new PermissionCallback() {
    @Override public void permissionGranted() { 
      boolean hasAccess = Tools.accessLocation(MainActivity.this); 
      Toast.makeText(MainActivity.this, "Access granted = " + hasAccess, Toast.LENGTH_SHORT).show(); 
    } 

    @Override public void permissionRefused() { 
      boolean hasAccess = Tools.accessLocation(MainActivity.this); 
      Toast.makeText(MainActivity.this, "Access granted = " + hasAccess, Toast.LENGTH_SHORT).show(); 
    } 
  }; 

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    Nammu.init(getApplicationContext()); 
  } 

  @Override protected void onResume() { 
    super.onResume(); 
    Nammu.permissionCompare(new PermissionListener() { 
      @Override public void permissionsChanged(String permissionRevoke) {
        //Toast is not needed as always either permissionsGranted() or permissionsRemoved() will be called 
        //Toast.makeText(MainActivity.this, "Access revoked = " + permissionRevoke, Toast.LENGTH_SHORT).show(); 
      } 

      @Override public void permissionsGranted(String permissionGranted) {
        Toast.makeText(MainActivity.this, "Access granted = " + permissionGranted,
            Toast.LENGTH_SHORT).show();
      } 

      @Override public void permissionsRemoved(String permissionRemoved) {
        Toast.makeText(MainActivity.this, "Access removed = " + permissionRemoved,
            Toast.LENGTH_SHORT).show();
      } 
    }); 
  } 

  @OnClick(R.id.buttonSpecialChangeSettings) public void clickButtChangeSettings() {
    if (Nammu.checkSpecialPermission(Manifest.permission.WRITE_SETTINGS)) {
      Toast.makeText(this, "Access granted", Toast.LENGTH_SHORT).show();
    } else { 
      Nammu.askForSpecialPermission(this, Manifest.permission.WRITE_SETTINGS,
          new PermissionCallback() { 
            @Override public void permissionGranted() { 
              Toast.makeText(MainActivity.this, "Access granted", Toast.LENGTH_SHORT).show();
            } 

            @Override public void permissionRefused() { 
              Log.d(TAG, "permissionRefused: ");
              Toast.makeText(MainActivity.this, "Access refused", Toast.LENGTH_SHORT).show();
            } 
          }); 
    } 
  } 

  @OnClick(R.id.buttonContacts) public void clickButtContacts() {
    //Lets see if we can access Contacts 
    if (Nammu.checkPermission(Manifest.permission.READ_CONTACTS)) {
      //We have a permission, easy peasy 
      boolean hasAccess = Tools.accessContacts(this);
      Toast.makeText(this, "Access granted = " + hasAccess, Toast.LENGTH_SHORT).show();
    } else { 
      //We do not own this permission 
      if (Nammu.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
        //User already refused to give us this permission or removed it 
        //Now he/she can mark "never ask again" (sic!) 
        Snackbar.make(mLayout, "Here we explain user why we need to know his/her contacts.",
            Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {
          @Override public void onClick(View view) {
            Nammu.askForPermission(MainActivity.this, Manifest.permission.READ_CONTACTS,
                permissionContactsCallback);
          } 
        }).show(); 
      } else { 
        //First time asking for permission 
        // or phone doesn't offer permission 
        // or user marked "never ask again" 
        Nammu.askForPermission(this, Manifest.permission.READ_CONTACTS, permissionContactsCallback);
      } 
    } 
  } 

  @OnClick(R.id.buttonLocation) public void clickButtLocation() {
    if (Nammu.checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
      boolean hasAccess = Tools.accessLocation(this);
      Toast.makeText(this, "Access granted fine= " + hasAccess, Toast.LENGTH_SHORT).show();
    } else { 
      if (Nammu.shouldShowRequestPermissionRationale(this,
          Manifest.permission.ACCESS_FINE_LOCATION)) {
        //User already refused to give us this permission or removed it 
        //Now he/she can mark "never ask again" (sic!) 
        Snackbar.make(mLayout, "Here we explain user why we need to know his/her location.",
            Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {
          @Override public void onClick(View view) {
            Nammu.askForPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION,
                permissionLocationCallback);
          } 
        }).show(); 
      } else { 
        //First time asking for permission 
        // or phone doesn't offer permission 
        // or user marked "never ask again" 
        Nammu.askForPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION,
            permissionLocationCallback);
      } 
    } 
  } 

  @OnClick(R.id.buttonStartFragmentActivity) public void clickStartActivityWithFragment() {
    Intent activity = new Intent(this, FragmentActivity.class);
    startActivity(activity);
  } 

  @Override public void onRequestPermissionsResult(int requestCode, String[] permissions,
      int[] grantResults) {
    Nammu.onRequestPermissionsResult(requestCode, permissions, grantResults);
  } 
} 

Download

Let's go over and download the project, or browse it from github.

No. Location Link
1. GitHub Direct Download
2. GitHub Browse

Thanks and Credit to the Original Creator of the above project Michal Tajchert

How to Run

  1. Download the project above.
  2. Create your application in android studio as normal.
  3. Replace the layouts and activities in your project with the ones in the sample folder.
  4. Make sure you edit your app level build.gradle to add the appropriate dependencies.

How do You Feel after reading this?

According to scientists, we humans have 8 primary innate emotions: joy, acceptance, fear, surprise, sadness, disgust, anger, and anticipation. Feel free to tell us how you feel about this article using these emotes or via the comment section. This feedback helps us gauge our progress.

Help me Grow.

I set myself some growth ambitions I desire to achieve by this year's end regarding this website and my youtube channel. Am halfway. Help me reach them by:




Recommendations


What do You Think


Previous Post Next Post