Search code examples
androidsqlitebroadcastreceiveralarmmanagerandroid-cursoradapter

Android: Alarm Manager; Nullpointer when accessing SQLiteOpenhelper in MainActivity from Broadcastreceiver class


I am busy working on a quotes app that will show the user a new motivational quote each morning at 9:00am.

I have this method in my MainActivity to pull and show the quote in a listView that shows the single quote. I know other examples of questions of this type exist but I have not seen one using database data and cursors or cursor adapters as I need to.

public void DailyQuoteDatabaseAccess(){

    SQLiteOpenHelper sqLiteOpenHelper = new SQLiteAssetHelper(this, DATABASE_NAME, null, DATABASE_VERSION);
    SQLiteDatabase SqlDb = sqLiteOpenHelper.getReadableDatabase();
    String rawQuery = "SELECT * FROM dailyQuoteTable ORDER BY RANDOM() LIMIT 1";
    Cursor cursor = SqlDb.rawQuery(rawQuery, null);
    DailyQuoteCursorAdapter DQCursorAdapter = new DailyQuoteCursorAdapter(this, cursor);
    this.mDailyQuoteListView.setAdapter(DQCursorAdapter);
}

I was calling this method from OnCreate and it works perfectly however it reloads each time the app is restarted. I am using AlarmManager to set the alarm to change the quote each morning at 9:00am and the alarm is firing but the problem lies in being able to run that intent in my MainActivity from the Broadcast Receiver class. Here is that class... blank because I can't figure out how to make it work.

package com.myapps.dailyquotes;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class TodaysQuoteAlarmReceiver  extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    MainActivity mainActivity = new MainActivity();
    mainActivity.DailyQuoteDatabaseAccess();

    }
}

Any help in this arena would be very much appreciated. Should I use an intent or an object of an entirely new class and can you please provide an example?

****11/22/18****

I forgot to add my custom cursor adapter... I think this may make a difference but I'm still not sure how to integrate it with Broadcast Receiver to pass the data I need...

public class DailyQuoteCursorAdapter extends CursorAdapter{

    public DailyQuoteCursorAdapter(Context context, Cursor c) {
        super(context, c, 0);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return LayoutInflater.from(context).inflate(R.layout.daily_quote_item, parent, false);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        TextView NameTextView = (TextView) view.findViewById(R.id.name);
        TextView NumberTextView = (TextView) view.findViewById(R.id.number);
        TextView DateTextView = (TextView) view.findViewById(R.id.date);
        TextView QuoteTextView = (TextView) view.findViewById(R.id.quote);

        String name = cursor.getString(cursor.getColumnIndexOrThrow("quote_main"));
        String number = cursor.getString(cursor.getColumnIndexOrThrow("number"));
        String date = cursor.getString(cursor.getColumnIndexOrThrow("date"));
        String quote = cursor.getString(cursor.getColumnIndexOrThrow("quote"));

        nameTextView.setText(name + " ");
        numberTextView.setText(String.valueOf(number) + ":");
        dateTextView.setText(String.valueOf(date));
        quoteTextView.setText(quote);

    }

}

This is my log cat for the null pointer...

Process: com.myapps.dailyquotes, PID: 15024
java.lang.RuntimeException: Error receiving broadcast Intent { act=android.net.conn.CONNECTIVITY_CHANGE flg=0x4000010 (has extras) } in com.myapps.dailyquotes.TodaysQuoteAlarmReceiver@d63089d
    at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$-android_app_LoadedApk$ReceiverDispatcher$Args_52497(LoadedApk.java:1323)
    at android.app.-$Lambda$aS31cHIhRx41653CMnd4gZqshIQ.$m$7(Unknown Source:4)
    at android.app.-$Lambda$aS31cHIhRx41653CMnd4gZqshIQ.run(Unknown Source:39)
    at android.os.Handler.handleCallback(Handler.java:790)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6494)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
 Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
    at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:152)
    at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.<init>(SQLiteAssetHelper.java:109)
    at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.<init>(SQLiteAssetHelper.java:129)
    at com.myapps.dailyquotes.MainActivity.DailyQuoteDatabaseAccess(MainActivity.java:403)
    at com.myapps.dailyquotes.TodaysQuoteAlarmReceiver.onReceive(TodaysQuoteAlarmReceiver.java:18)

The receiver does attempt to run the method from MainActivity so I know it's making the connection but the error is coming from this line inside the DailyQuoteDatabaseAccess method inside of the MainActivity class:

 SQLiteOpenHelper sqLiteOpenHelper = new SQLiteAssetHelper(this, DATABASE_NAME, null, DATABASE_VERSION);

Solution

  • For Daily Quotes, I have do something....

    public void setAlarm() {
    // Quote in Morning at 08:32:00 AM
        Calendar calendar = Calendar.getInstance();
    
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 32);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
    
        Calendar cur = Calendar.getInstance();
    
        if (cur.after(calendar)) {
            calendar.add(Calendar.DATE, 1);
        }
    
        Intent myIntent = new Intent(context, DailyReceiver.class);
        int ALARM1_ID = 10000;
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                context, ALARM1_ID, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
    }
    

    now make function in database class to get random quote in database class

    public String getAlarmQuote(int id, String table) {
       String quote = "", author = "";
       SQLiteDatabase db = this.getReadableDatabase();
       Cursor cursor = db.query(table, new String[]{QUOTE_ID, QUOTE_QUOTE, QUOTE_AUTHOR}, QUOTE_ID + "=?", new String[]{String.valueOf(id)}, null, null, null, null);
       if (cursor != null && cursor.getCount() > 0) {
           if (cursor.moveToFirst()) {
               quote = cursor.getString(cursor.getColumnIndex(QUOTE_QUOTE));
               author = cursor.getString(cursor.getColumnIndex(QUOTE_AUTHOR));
           }
       }
       db.close();
       return quote;
    }
    

    and to get this reciver

    public class DailyReceiver extends BroadcastReceiver {
    
    DatabaseHelper databaseHelper;
    
    @Override
    public void onReceive(Context context, Intent intent) {String quote ;
    long when = System.currentTimeMillis();
    NotificationManager notificationManager = (NotificationManager) context
            .getSystemService(Context.NOTIFICATION_SERVICE);
    
    Intent notificationIntent = new Intent(context, DailySpecialActivity.class);
    notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
            notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
    Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    
    // get your quote here
       int max = databaseHelper.getTotalQuotes();  // here you have to put total number of quotes in your database table
       Random r = new Random();
       int randomNumber = r.nextInt(max - 1 + 1) + 1;
       quote = databaseHelper.getAlarmQuote(randomNumber, YourTableName);
    
    
    NotificationCompat.Builder mNotifyBuilder = new NotificationCompat.Builder(
            context).setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle("My Quotes")
            .setContentText(quote).setSound(alarmSound)
            .setAutoCancel(true).setWhen(when)
            .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher))
            .setStyle(new NotificationCompat.BigTextStyle().bigText(quote))
            .setContentIntent(pendingIntent)
            .setVibrate(new long[]{1000, 1000, 1000, 1000, 1000});  // Declair VIBRATOR Permission in AndroidManifest.xml
    notificationManager.notify(5, mNotifyBuilder.build());
       }
    }
    

    Here when you reboot your phone you also need to restart your alarm, so I have do

    public class WakeUpAlarmReceiver extends BroadcastReceiver {
    
    @Override
    public void onReceive(Context context, Intent intent) {
    
    if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
        // Quote in Morning
        Calendar calendar = Calendar.getInstance();
    
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 30);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
    
        Calendar cur = Calendar.getInstance();
    
        if (cur.after(calendar)) {
            calendar.add(Calendar.DATE, 1);
        }
        Intent myIntent = new Intent(context, DailyReceiver.class);
        int ALARM1_ID = 10000;
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, ALARM1_ID, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
      }
     }
    }
    

    Finally here is your AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.nils.myquotesapp">
    <!--you must need this three permissions-->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.VIBRATE" />
    
    <application
      android:name="com.nils.myquotesapp.QuotesApplication"
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:largeHeap="true"
      android:supportsRtl="true"
      android:theme="@style/AppTheme">
      <activity
        android:name="com.nils.myquotesapp.Activity.MainActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    
    <receiver
        android:name="com.nils.myquotesapp.DailyReceiver"
        android:enabled="true" />
    
    <receiver
        android:name="com.nils.myquotesapp.WakeUpAlarmReceiver"
        android:enabled="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <category android:name="android.intent.category.DEFAULT" />
            <action android:name="android.intent.action.QUICKBOOT_POWERON" />
        </intent-filter>
    </receiver>
    
    </application>
    <meta-data
      android:name="com.google.android.gms.version"
      android:value="@integer/google_play_services_version" />
    </manifest>