1

I am trying to call a method in my MainActivity class from another class using an AlarmManager.

I found this question which helped me call the method but when the app is closed, and the AlarmManager is still running it gives a NullPointerException error.

Another solution which would work for me is to have getBatteryLevel() inside my AlarmRec class. When I try this I get the error - Cannot resolve method registerReceiver(null, android.content.IntentFilter).

My MainActivity class:

package com.ds.shutdown;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.Notification; 
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.BatteryManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Calendar;

public class MainActivity extends Activity {

    int userLvl;
    int batteryLvl;
    static MainActivity mainActivity;
    private AlarmManager alarmMgr;
    private PendingIntent alarmIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mainActivity = this;

        batteryLvl = getBatteryLevel();
        String bat = Integer.toString(batteryLvl);
        TextView textViewTwo = (TextView) findViewById(R.id.textBatteryLvl);
        textViewTwo.setText("Battery level: " + bat);

        userLvl = loadSavedPreferences();
        TextView textView = (TextView) findViewById(R.id.textUserLvl);
        textView.setText("User entry: " + Integer.toString(userLvl));


        if (getIntent() != null && getIntent().getExtras() != null && getIntent().getExtras().getBoolean("CALL_METHOD", false)) {
             shutdown();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
         // Inflate the menu; this adds items to the action bar if it is present.
         getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    public void shutdown() {
        Toast.makeText(this, "In shutdown method", Toast.LENGTH_SHORT).show();

        batteryLvl = getBatteryLevel();
        String bat = Integer.toString(batteryLvl);
        TextView textViewTwo = (TextView) findViewById(R.id.textBatteryLvl);
        textViewTwo.setText("Battery level: " + bat);

        userLvl = loadSavedPreferences();

        if (batteryLvl <= userLvl) {
            try {
                 Process process = Runtime.getRuntime().exec(new String[]{"su", "-c", "reboot -p"});
                 process.waitFor();
            } catch (Exception ex) {
                 ex.printStackTrace();
            }
        }
    }

    public int getBatteryLevel() {
        Intent batteryIntent = registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);

        return level;
    }

    //OK button onClick
    public void btnPress(View v) {
         EditText mEditText = (EditText) findViewById(R.id.userLvl); //Look at text box
         String userLvlStr = mEditText.getText().toString(); //Take contents and set to string

        if (userLvlStr.matches("")){
            Toast.makeText(this, "Invalid Entry", Toast.LENGTH_SHORT).show();
            return;
        }

        userLvl = Integer.parseInt(userLvlStr); //Convert that string to an int
        saveSavedPreferences(userLvl);  //Save that int

        setContentView(R.layout.activity_main); //Look at main activity
        TextView textView = (TextView) findViewById(R.id.textUserLvl);     //Look at text view
        textView.setText("User entry: " + userLvlStr);  //Change text view to show user entry

        batteryLvl = getBatteryLevel();
        String bat = Integer.toString(batteryLvl);
        TextView textViewTwo = (TextView) findViewById(R.id.textBatteryLvl);
        textViewTwo.setText("Battery level: " + bat);

        alarmMethod();
    }

    //Saves variable(s) across app close
    private void saveSavedPreferences(int userLvl){
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);    //Create SharedPreferences object
        SharedPreferences.Editor editor= sharedPref.edit();  //Now get editor
         editor.putInt("userLvl", userLvl); //Put in variable
         editor.commit();    //Save variable(s)
    }

    //Loads variable(s) across app close
    private int loadSavedPreferences() {
         SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
         userLvl = sharedPrefs.getInt("userLvl", 0);
        return userLvl; //Using return since only one variable is needed
    }

    //Set alarm
    private void alarmMethod() {

        alarmMgr = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); //Create Alarm Manager
        Intent intent = new Intent(this, AlarmRec.class);   //Set intent
        alarmIntent = PendingIntent.getBroadcast(this, 0, intent, 0);

        Calendar calendar = Calendar.getInstance(); 

        //setRepeating() lets you specify a precise custom interval--in this case, 20 seconds

        alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
            1000 * 10, alarmIntent);

        Toast.makeText(MainActivity.this, "Alarm set", Toast.LENGTH_LONG).show();
    }

    public void cancelAlarm(View v2) {

        // If the alarm has been set, cancel it.
        if (alarmMgr != null) {
             alarmMgr.cancel(alarmIntent);
        }

        TextView textView = (TextView) findViewById(R.id.textUserLvl);
        textView.setText("User entry: ");

    }

    public static MainActivity getInstance(){
         return mainActivity;
    }
}

My Alarm Receiver class:

package com.ds.shutdown;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.widget.Toast;

public class AlarmRec extends BroadcastReceiver {

    //@Override
    public void onReceive(Context context, Intent intent) {
        //Call shutdown
        Toast.makeText(MainActivity.getInstance(), "Calling shutdown", Toast.LENGTH_SHORT).show();

        if (MainActivity.getInstance() != null) {
             MainActivity.getInstance().shutdown();
        }
        else {
            Intent i = new Intent(context, MainActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            //I also tried i.addFlags
            i.putExtra("CALL_METHOD", true);
            context.startActivity(i);
        }
    }
}
Community
  • 1
  • 1
  • You can't do that. When app is close then MainActivity.getInstance() return null. You can write the method within Broadcast . – Yajneshwar Mandal Jan 28 '15 at 20:26
  • I can't write the method in my BroadcastReceiver because I need to get the battery level which I can't figure out how to do withing BroadcastReceiver. –  Jan 29 '15 at 09:36

2 Answers2

0

Because when your app is closed MainActivity.getInstance() returns null.

You should do methodToCall() in your BroadcastReceiver (if the method doesn't take long) or call Service from there, where you do methodToCall().

Another way is to modify your Receiver like this

Toast.makeText(context, "Calling shutdown", Toast.LENGTH_SHORT).show();

    if (MainActivity.getInstance() != null) {
        MainActivity.getInstance().methodToCall();
    } 
    else {
        Intent i = new Intent(context, MainActivity.class);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        i.putExtra("CALL_METHOD", true);
        context.startActivity(i);
    }

And MainActivity in onCreate check for CALL_METHOD extra;

if (getIntent() != null && getIntent().getExtras() != null && getIntent().getExtras().getBoolean("CALL_METHOD", false)) {
    methodToCall();
}
kroky
  • 195
  • 2
  • 14
  • Do you mean call methodToCall() from the onReceive method? –  Jan 28 '15 at 20:12
  • The app shuts down the phone at a user defined battery level, re-writing the method into onReceive would work but updating the battery level doesn't work in the onReceive for some reason. –  Jan 28 '15 at 20:27
  • I've updated my code in the question with my getBatteryLevel() method. When I copy that code into my AlarmRec class I get the 'Cannot resolve method 'registerReceiver(null, android.content.IntentFilter)' –  Jan 28 '15 at 20:33
  • Because in BroadcastReceiver you should call it like context.registerReceiver(null, android.content.IntentFilter). Updated my answer with another solution. – kroky Jan 28 '15 at 20:37
  • Thanks for all your help but now the app crashes at start-up with the error - Attempt to invoke virtual method 'boolean android.os.Bundle.getBoolean(java.lang.String)' on a null object reference –  Jan 28 '15 at 20:56
  • It's because you still need to check if getIntent() != null and getIntent().getExtras() != null. – kroky Jan 29 '15 at 13:38
  • I still can't get it working - I have used the code you have put in your answer - but like I mentioned 'null object reference' on the boolean. I don't where the if (getIntent() != null && getIntent().getExtras() != null) should go? –  Jan 29 '15 at 19:31
  • Thanks again but I had the error - android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want? - So added the line 'i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);' but now I get the NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference error. Really sorry, but I don't see what I'm doing wrong, I really appreciate all this help. –  Jan 30 '15 at 21:07
  • Even if you could tell me how to get the battery level in my AlarmRec class that would work for me. –  Feb 01 '15 at 19:25
  • So the problem is still because of your MainActivity.getInstance() method, as already user3282164 said, that's not the right way to do it. In AlarmRec you're calling Toast.makeText(MainActivity.getInstance(), "Calling shutdown", Toast.LENGTH_SHORT).show(); before you even check if MainActivity.getInstance() != null. I edited my answer. And not intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), but i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); – kroky Feb 04 '15 at 00:14
  • It's always useful, to watch exception stacktraces in logcat, because there you can see at which line the exception was thrown. – kroky Feb 04 '15 at 00:20
  • Ah right, I haven't tried this yet, I'll try it tonight after work, but it was so obvious, I didn't even pay attention to my toast line. Thanks a lot! –  Feb 04 '15 at 10:21
0
  1. Do not expose your main activity through a static method like a singleton.
  2. Your activity getInstance() method will return null because it has probably been popped off the back stack (or if activity has been killed). Do not rely on your activity being present and is reference to be valid after you have backgrounded your app.
  3. I don't understand what functionality you want in that method that the activity defines. You should be able to put that in the broadcast receiver. If you need UI to be shown, launch your activity again (in which case the UI would show up again).
sr09
  • 720
  • 5
  • 26
  • I've commented on the other answer why my code can't, well why I can't get it working in the AlarmRec class. (You probably started typing your answer before the comment). –  Jan 28 '15 at 20:37