Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android

Calling activity's methods from a separate class.

So I've been playing around with Android M style permissions and I have a working demo so long as I do everything from within the MainActivity. I felt that it looked to cumbersome and wanted to split that in to multiple files so it looked a little cleaner and could be more easily maintained.

The problem is by moving the permissions checking into its own file, I am having trouble now getting my permissions checker to go back to MainActivity and launch the demo() method. Where I need to call back to main activities demo methos is on line 73 and line 114 of the PermissionsChecker.java

NOTE: I followed this tutorial on Android Permissions

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

    <!-- // Permissions -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.CAMERA" />

    <!-- // Features -->
    <uses-feature android:name="android.hardware.camera" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

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

</manifest>
MainActivity.java
package me.johnweland.androidrtp;

import android.app.Activity;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (Build.VERSION.SDK_INT >= 23) {
            // Marshmallow+
            PermissionChecker permissions = PermissionChecker.getInstance(this);
            permissions.permissionsCheck();
        } else {
            // Pre-Marshmallow
            demo();
        }

    }

    protected void demo() {
        Toast.makeText(this, "Demo toast", Toast.LENGTH_LONG).show();
    }
}
PermissionsChecker.java
package me.johnweland.androidrtp;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by jweland on 12/10/2015.
 */

public class PermissionChecker {
    private static final String TAG = PermissionChecker.class.getSimpleName();
    final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 0;

    private Activity mainActivity;
    private static PermissionChecker instance = null;
    private PermissionChecker(Activity activity) {
        this.mainActivity = activity;
    }

    static public PermissionChecker getInstance(Activity activity) {
        if (instance == null) {
            instance = new PermissionChecker(activity);
            return instance;
        } else {
            return instance;
        }
    }

    protected static boolean hasPermissions;

    @TargetApi(Build.VERSION_CODES.M)
    protected void permissionsCheck(){
        List<String> permissionsNeeded = new ArrayList<String>();

        final List<String> permissionsList = new ArrayList<String>();
        // Add permission check for any permission that is not NORMAL_PERMISSIONS
        if(!addPermission(permissionsList, Manifest.permission.RECORD_AUDIO))
            permissionsNeeded.add(mainActivity.getString(R.string.permission_microphone));
        if(!addPermission(permissionsList, Manifest.permission.CAMERA))
            permissionsNeeded.add(mainActivity.getString(R.string.permission_camera));

        if(permissionsList.size() > 0) {
            if(permissionsNeeded.size() > 0) {
                // Need Rationale
                String message = mainActivity.getString(R.string.permission_grant_message) + permissionsNeeded.get(0);
                for (int i = 1; i < permissionsNeeded.size(); i++)
                    message = message + "\n" +permissionsNeeded.get(i);
                showMessageOKCancel(message,
                        new DialogInterface.OnClickListener() {
                            @TargetApi(Build.VERSION_CODES.M)
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                mainActivity.requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                                        REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                            }
                        });
                return;
            }
            mainActivity.requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                    REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
            return;
        }
        // launch method demo();
    }

    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(mainActivity)
                .setMessage(message)
                .setPositiveButton(R.string.dialog_ok_button_text, okListener)
                .setNegativeButton(R.string.dialog_cancel_button_text, null)
                .create()
                .show();
    }

    @TargetApi(Build.VERSION_CODES.M)
    private boolean addPermission(List<String> permissionsList, String permission) {
        if (mainActivity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
            permissionsList.add(permission);
            // Check for Rationale Option
            if (!mainActivity.shouldShowRequestPermissionRationale(permission))
                return false;
        }
        return true;
    }


    @TargetApi(Build.VERSION_CODES.M)
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
            {
                Map<String, Integer> perms = new HashMap<String, Integer>();
                // Initial
                perms.put(Manifest.permission.RECORD_AUDIO, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
                // Fill with results
                for (int i = 0; i < permissions.length; i++)
                    perms.put(permissions[i], grantResults[i]);
                // Check for RECORD_AUDIO
                if (perms.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
                        && perms.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                    // All Permissions Granted
                    // launch method demo();
                }
                else {
                    // Permission Denied
                    Toast.makeText(mainActivity, R.string.permission_denied_message, Toast.LENGTH_SHORT).show();
                }
            }
            break;
            default:
                mainActivity.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

1 Answer

Ben Deitch
STAFF
Ben Deitch
Treehouse Teacher

In PermissionChecker, make mainActivity of type MainActivity instead of Activity and you should be able to call mainActivity.demo();

Thanks, I will give this a try as soon as my system is back online... (had to format the home PC, Android Studio is running updates now).

UPDATE ::

So I have updated the code as you say, (at least I hope I did). my method 'demo()' simply calls a Toast when it's called that says "Demo Text" at length long. When I run the app for the first time It calls out for permissions and get its permissions. After was it goes the the activity view but I don't see my toast from demo(). However if I close the app and re run it since it doesn't need permissions now it goes to the activity view and launches demo and I see the Toast.

the only part of my code that has changed now is

PermissionChecker.java
public class PermissionChecker {
    private static final String TAG = PermissionChecker.class.getSimpleName();
    final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 0;

    private MainActivity mainActivity;
    private static PermissionChecker instance = null;
    private PermissionChecker(MainActivity activity) {
        mainActivity = activity;
    }

    static public PermissionChecker getInstance(MainActivity activity) {
        if (instance == null) {
            instance = new PermissionChecker(activity);
            return instance;
        } else {
            return instance;
        }
    }

    ...
   //rest of code left out for brevity 
}

I am of course called "mainActivity.demo();" where I previously had the comment stating as such.

Ben Deitch
Ben Deitch
Treehouse Teacher

In permissionsCheck, it looks like unless permissionsList.size() is less than or equal to 0 it'll return before getting to mainActivity.demo().

Yeah I've tried removing the return; and it doesn't work, I tried adding an 'else' I tried adding an = to the > nothing seems to work. to get that call.

Ben Deitch
Ben Deitch
Treehouse Teacher

Did you remove both returns?

        if(permissionsList.size() > 0) {
            if(permissionsNeeded.size() > 0) {
                // Need Rationale
                String message = mainActivity.getString(R.string.permission_grant_message) + permissionsNeeded.get(0);
                for (int i = 1; i < permissionsNeeded.size(); i++)
                    message = message + "\n" +permissionsNeeded.get(i);
                showMessageOKCancel(message,
                        new DialogInterface.OnClickListener() {
                            @TargetApi(Build.VERSION_CODES.M)
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                mainActivity.requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                                        REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                            }
                        });
            } else {
                mainActivity.requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                    REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
            }
        }
        // launch method demo();

I hadn't but tried it and it works, however I see my toast from demo(); fire off before I grant permissions, shouldn't that happen after? I think I am confused as to 'return;' and how the code runs. I thought return meant now that you have done this thing return to do the next thing.

Ben Deitch
Ben Deitch
Treehouse Teacher

As it is now, it will build and show the dialog, and then jump right back into permissionsCheck and show the Toast. If you want it to happen after you've clicked to grant permissions, you should put it in the Dialog's onClick method. Also, once a return is called, nothing else in that method will execute. It returns immediately to the calling funtion (which in this case is onCreate).

OH ! OK I was thinking return mean basically to continue down the code. I've got to write up a for count in the dialog that fires off once the last permission has been granted.

Ben Deitch, Thanks for all your help!

So I realized I have an entire block of code here onRequestPermissionsResult() {} that should fire off after the user either grants or denies the permissions. I wonder why its not firing.

@TargetApi(Build.VERSION_CODES.M)
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
            {
                Map<String, Integer> perms = new HashMap<String, Integer>();
                // Initial
                perms.put(Manifest.permission.RECORD_AUDIO, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
                // Fill with results
                for (int i = 0; i < permissions.length; i++)
                    perms.put(permissions[i], grantResults[i]);
                // Check for RECORD_AUDIO
                if (perms.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
                        && perms.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                    // All Permissions Granted
                    mainActivity.demo();
                }
                else {
                    // Permission Denied
                    Toast.makeText(mainActivity, R.string.permission_denied_message, Toast.LENGTH_SHORT).show();
                }
            }
            break;
            default:
                mainActivity.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

UPDATE:: Ben Deitch I moved onRequestPermissionsResult() {} out of the permission class and back in to MainActivity (cause its a callback) and BOOM problem solved!! :) thanks again for you help!