javaandroidandroid-broadcastreceiver

Not Able to modify an Activity from my Broadcast Receiver of Alarm Manager


I want to modify my plan which is an app database everyday at midnight. Also I want to modify my Activity which showing certain numbers that are supposed to change daily according to the plan information. I am able using the Alarm Manager to update the database but I am unable to change the textviews of the Activity. It is giving me this error: java.lang.ClassCastException: android.app.ReceiverRestrictedContext cannot be cast to com.example.bleh.myapplication.feature2

Here is my Alarm Receiver Class:

package com.example.bleh.myapplication;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.example.bleh.myapplication.DB.AppDatabase;
import com.example.bleh.myapplication.DB.Plan;
import com.example.bleh.myapplication.DB.User;
import com.example.bleh.myapplication.Utils1.FormulaUtils;
import com.github.lzyzsd.circleprogress.DonutProgress;

public class AlarmReceiver extends BroadcastReceiver {

    private static final String DEBUG_TAG = "AlarmReceiver";
    public AppDatabase mydb;
    Plan plan;

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Alarm worked.", Toast.LENGTH_LONG).show();
        mydb = AppDatabase.getInstance(context);
        final long planid = intent.getExtras().getLong("planid");
        final long userid = intent.getExtras().getLong("uid");
        final String requirements = intent.getExtras().getString("requirements");
        Log.wtf("PlanId: ",planid+"");
        Log.wtf("UserId: ",userid+"");
        Log.wtf("Requirements",requirements);
        plan = mydb.getPlanDao(context).getPlanById((int) planid);
        final User user = mydb.getUserDao(context).getUserById((int) userid);
        plan.setCurrentWeight(FormulaUtils.reCalculateWeight(plan.getCurrentWeight(), Double.parseDouble(requirements)));
        plan.setBmr(Double.parseDouble(FormulaUtils.calculateBmr(user.getSex(), plan.getCurrentWeight(), user.getHeight(), user.getBirthDay())));
        plan.setNbOfDays(plan.getNbOfDays() - 1);
        mydb.getPlanDao(context).update(plan);
        TextView requirement = ((feature2)context).findViewById(R.id.requirements);
        TextView Days = ((feature2)context).findViewById(R.id.days);
        DonutProgress DailyProgress = ((feature2)context).findViewById(R.id.donut_progress);
        requirement.setText(FormulaUtils.CalulcateDailyRequirements(plan.getWorkoutPerWeek(), plan.getBmr()));
        Days.setText(plan.getNbOfDays()+"");
        int progress = 0;
        DailyProgress.setProgress((float) progress);
//        Intent newIntent = new Intent(context, feature2.class);
//        newIntent.putExtra("uid", userid);
//        newIntent.putExtra("planid", planid);
//        context.startActivity(newIntent);
    }

}

Here is my Feature Activity(part of it):

public class feature2 extends AppCompatActivity {

    public AppDatabase mydb;
    TextView BMR,requirements,days;
    Button addfood,addex,nextday;
    LinearLayout mainLayout;
    Button Meas,Bluetooth;
    DonutProgress donutProgress;
    Plan plan;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_feature2);
        Intent intent = getIntent();
        requirements = findViewById(R.id.requirements);
        donutProgress = findViewById(R.id.donut_progress);
        days = findViewById(R.id.days);
        final long planid = intent.getExtras().getLong("planid");
        final long userid = intent.getExtras().getLong("uid");
        mydb = AppDatabase.getInstance(feature2.this);
        plan = mydb.getPlanDao(feature2.this).getPlanById((int) planid);
        requirements.setText(FormulaUtils.CalulcateDailyRequirements(plan.getWorkoutPerWeek(), plan.getBmr()));
        Intent intent1 = new Intent(this, AlarmReceiver.class);
        intent1.putExtra("uid", userid);
        intent1.putExtra("planid", planid);
        intent1.putExtra("requirements",requirements.getText().toString());
        Calendar updateTime = Calendar.getInstance();
        updateTime.setTimeZone(TimeZone.getTimeZone("GMT"));
        updateTime.set(Calendar.HOUR_OF_DAY, 20);
        updateTime.set(Calendar.MINUTE, 03);
        updateTime.set(Calendar.SECOND,0);
        Date milliseconds = updateTime.getTime();
        Log.wtf("millisec",milliseconds+"");
        long millis = milliseconds.getTime();
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
                intent1, PendingIntent.FLAG_ONE_SHOT);

        AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmManager.set(AlarmManager.RTC_WAKEUP, millis , pendingIntent);
        Toast.makeText(this, "Alarm set", Toast.LENGTH_LONG).show();
   }
}

Solution

  • This line make your app crash

    TextView requirement = ((feature2)context).findViewById(R.id.requirements);
    

    context is an instance of ReceiverRestrictedContext, feature2 is an instance of AppCompatActivity whose parent is ContextThemeWrapper. feature2 is not an instance of ReceiverRestrictedContext, that why your app throws an ClassCastException and make your app crash.

    Solution: There are many ways to solve your problem, here is a simple one.

    Step 1: In AlarmReceiver class, start feature2 activity with FLAG_ACTIVITY_SINGLE_TOP | FLAG_ACTIVITY_CLEAR_TOP.

    public class AlarmReceiver extends BroadcastReceiver {
    
        private static final String DEBUG_TAG = "AlarmReceiver";
        public AppDatabase mydb;
        Plan plan;
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "Alarm worked.", Toast.LENGTH_LONG).show();
            mydb = AppDatabase.getInstance(context);
            final long planid = intent.getExtras().getLong("planid");
            final long userid = intent.getExtras().getLong("uid");
            final String requirements = intent.getExtras().getString("requirements");
            Log.wtf("PlanId: ",planid+"");
            Log.wtf("UserId: ",userid+"");
            Log.wtf("Requirements",requirements);
            plan = mydb.getPlanDao(context).getPlanById((int) planid);
            final User user = mydb.getUserDao(context).getUserById((int) userid);
            plan.setCurrentWeight(FormulaUtils.reCalculateWeight(plan.getCurrentWeight(), Double.parseDouble(requirements)));
            plan.setBmr(Double.parseDouble(FormulaUtils.calculateBmr(user.getSex(), plan.getCurrentWeight(), user.getHeight(), user.getBirthDay())));
            plan.setNbOfDays(plan.getNbOfDays() - 1);
            mydb.getPlanDao(context).update(plan);
    
            // Calculate all data before sending to feature2 activity
            String requirement = FormulaUtils.CalulcateDailyRequirements(plan.getWorkoutPerWeek(), plan.getBmr());
            String day = plan.getNbOfDays() + "";
            float progress = 0F;
    
            // Start feature2 activity with updated data
            Intent updateFeature2Intent = new Intent(context, feature2.class);
            updateFeature2Intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); // Add this flag
            newIntent.putExtra("requirement", requirement);
            newIntent.putExtra("day", day);
            newIntent.putExtra("progress", progress);
            context.startActivity(newIntent);
        }
    }
    

    Step 2: If feature2 activity is on backstack and visible to user, onNewIntent will get called, in that case get the data and update your UI there.

    public class feature2 extends AppCompatActivity {
    
        ...
    
        // Add this method
        @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
    
            // Get data from intent
            String requirement = intent.getStringExtra("requirement");
            String day = intent.getStringExtra("day");
            float progress = intent.getFloatExtra("progress", 0F);
    
            // Update UI
            TextView requirements = findViewById(R.id.requirements);
            TextView Days = findViewById(R.id.days);
            DonutProgress dailyProgress = findViewById(R.id.donut_progress);
    
            requirements.setText(requirement);
            Days.setText(day);
            dailyProgress.setProgress(progress);
        }
    }