5

I currently have a Fragment that has several Buttons and contains an onClickListener. Each time one of those buttons are clicked, a counter variable is incremented by 1, and is set as the text for a TextView in another Fragment, using SharedPreferences.

The counter will stay the same even after the app is completely closed, and will appear in subsequent runs of the app.

My new goal is to reset the counters back to 0 at the end of each day (23:59:00 for the time, to be exact).

I decided to avoid a Google search to figure this out, and found TimerTask, Calendar, Timer, and Date APIs on the Android Developer docs; I tried to get this to work with those APIs. Unfortunately it's not working out the way I planned. The variables are set back to 0, but they stay at zero and will only increment up to 1, and go back to 0 every time I exit the app.

Is there a better way to approach this? Or is my method sufficient, and I just need to adjust/change some of the code?

One of the problems might be where I'm changing the counter variable reference as well (and if so, where should I change it)?

Here is what I attempted:

FirstFragment

 @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflating the layout
        View v = inflater.inflate(R.layout.starting_fragment, container, false);

        //Instantiate new Timer
        Timer timer = new Timer();
        // Creates a Calendar object that specifies a specific time of day
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(System.currentTimeMillis());
        cal.set(Calendar.HOUR_OF_DAY, 20);
        cal.set(Calendar.MINUTE, 57);
        cal.set(Calendar.SECOND, 00);
        cal.set(Calendar.MILLISECOND, 00);

        // Instantiate a day object and use the time of day from cal object as its data
        Date date = cal.getTime();

        TimerTask tt = new TimerTask() {
            // Sets the counter variables back to 0
            @Override
            public void run() {
                COUNT_OOL = 0;
                COUNT_WTE = 0;
                COUNT_BLO = 0;
                COUNT_BLK = 0;
                COUNT_HBL = 0;
                COUNT_GRN = 0;
                COUNT_MTE = 0;

            }
        };
        // Resets the counter variables (to 0) at the time specified by the date object
        timer.schedule(tt, date);

        // Stores count for each button back into their respective count variable
        // Initializes the value from previous runs of app to subsequent runs of app
        // This way, count variables will never get set back to 0 after onDestroy()
        COUNT_OOL = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("oolongCount", 0);
        COUNT_WTE = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("whiteCount", 0);
        COUNT_BLO = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("bloomingCount", 0);
        COUNT_BLK = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("blackCount", 0);
        COUNT_HBL = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("herbalCount", 0);
        COUNT_GRN = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("greenCount", 0);
        COUNT_MTE = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("mateCount", 0);

The onClick method that increments the counter variables:

 @Override
    public void onClick(View view) {
        int id = view.getId();
        /*
         * Use the View interface with OnClickListener to get the Button ID's
         * Then you can run a switch on the Buttons (because normally switches
         * cannot be run on buttons
         */

        if (id == R.id.tea_type1) {
            Builder oolongBuilder = new AlertDialog.Builder(StartingFragment.this.getActivity(),
                    AlertDialog.THEME_HOLO_LIGHT);

            oolongBuilder.setPositiveButton("Hot",
                    //Starts OolongTeaActivity for hot tea when clicked
                    new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface arg0, int arg1) {
                            Intent i = new Intent(StartingFragment.this.getActivity(),
                                    OolongTeaActivity.class);
                            StartingFragment.this.getActivity().startActivity(i);
                        }
                    });

            oolongBuilder.setNeutralButton("Iced",

                    new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent i = new Intent(StartingFragment.this.getActivity(),
                                    ColdOolongTeaActivity.class);
                            StartingFragment.this.getActivity().startActivity(i);

                        }
                    });

            oolongBuilder.setTitle("Oolong Tea");
            oolongBuilder.setMessage("How Do You Like Your Tea?");

            AlertDialog oolongDialog = oolongBuilder.create();
            oolongDialog.show();

            COUNT_OOL++;
            SharedPreferences pref1 = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE);
            SharedPreferences.Editor editor1 = pref1.edit();
            editor1.putInt("oolongCount", COUNT_OOL);
            editor1.commit();

        }

SecondFragment (sets the counters as the text for TextViews):

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.fragment_tea_counter, container, false);

        oolongCounterText = (TextView) rootView.findViewById(R.id.oolong_counter_tv);

        SharedPreferences pref1 = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE);
        Integer counter1 = pref1.getInt("oolongCount", 0);
        String s1 = String.valueOf(counter1);
        oolongCounterText.setText(s1);
freddiev4
  • 2,501
  • 2
  • 26
  • 46
  • You want to use `AlarmManager` to schedule your nightly counter reset. – Mike M. Jan 10 '15 at 02:31
  • @MikeM. How would I do this? I'm looking at the documentation right now and so it seems I'd have to start off with something like `AlarmManager am = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE); am.setExact(AlarmManager.RTC, System.currentTimeMillis(), pendingintent);` – freddiev4 Jan 10 '15 at 03:10
  • @MikeM. I've looked through the documentation, but the thing I don't understand is the PendingIntent. I'm not trying to use an intent, because I'm not using any activities. This updating of variables is between two Fragments only. Could you explain this a little more for me? – freddiev4 Jan 10 '15 at 18:07
  • The easiest thing to do is probably to implement a BroadcastReceiver that updates the SharedPreferences setting in `onReceive()`, register it in the manifest, and acquire the PendingIntent with `getBroadcast()`. – Mike M. Jan 10 '15 at 18:11
  • @MikeM. I've created a BroadCastReceiver class and set up the AlarmManager in my first Fragment. I think I'm almost there but I'm unsure what to do with the SharedPreferences in my Broadcast class, as it relates to the counters back in the Fragment. So I'd like some more help on this... [FirstFragment with Alarmanager](http://pastebin.com/xteWcm7c) && [Broadcast Activity](http://pastebin.com/PThHD0Ez) – freddiev4 Jan 14 '15 at 00:37
  • `The variables are set back to 0, but they stay at zero and will display as 1 (max) in the Fragment that displays the counts.` What? – Athena Mar 06 '15 at 15:59
  • @Athena edited for clarity – freddiev4 Mar 06 '15 at 16:54
  • Also, you should get the preference manager once, and reuse it, when you update a collection of prefs. – rds Mar 12 '15 at 00:27
  • Also upper case is the naming convention for constants, not variables. – rds Mar 12 '15 at 00:29
  • Also, your buttons should use the same listener, that's what the `which` parameter is useful for. – rds Mar 12 '15 at 00:30

5 Answers5

3

I would personally look at using the AlarmManager with the Calendar to set the time. You will then fire off a Service to do everything you need to do.

Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 0);
PendingIntent pi = PendingIntent.getService(context, 0,
            new Intent(context, MyService.class),PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                                AlarmManager.INTERVAL_DAY, pi);

Replace MyService with an actual Service, When the service starts it can: 1) Reset the number back to 0 2) Check if the app is running to see if you need to update the textboxes immediately or if it okay to wait for the user to launch the app 3) stop the service

Things to investigate before you follow this code:

Make sure the AlarmManager is right for you, a repeating alarm will NOT run after a reboot (Thanks to Jawnnypoo for clarifying this) Please see his comment below in which he links to a BroadcastReceiver so that the AlarmManager will run after a reboot.

Community
  • 1
  • 1
apmartin1991
  • 3,064
  • 1
  • 23
  • 44
  • 2
    This is a good solution to this issue, and I can tell you with 100% certainty that a repeating alarm does NOT run after a reboot. You need to re-register your repeating alarm at reboot time by setting up a broadcast receiver, and setting the permission in your manifest. See more examples here: http://stackoverflow.com/questions/26138587/android-alarm-manager-set-repeating-at-specific-timing – Jawnnypoo Mar 10 '15 at 18:19
  • 1
    Thanks for correcting me, I have edited my reply with your advice (I linked you in the answer) – apmartin1991 Mar 11 '15 at 09:34
  • 1
    I think using a `BroadcasterReceiver` on boot to register the alarm is an overkill. Maybe the app won't even be used for days. – rds Mar 12 '15 at 00:05
3

Maybe just store the day of the year and compare with the current day of the year.

dayOfYear = DateFormat.format("D", new Date())

if (dayOfYear != lastResetDay) {
    resetCounters();
}
rockerBOO
  • 2,920
  • 2
  • 16
  • 22
  • Bug if the app is opened exactly one year after the use. – rds Mar 13 '15 at 17:23
  • The main question is not answered: when do you performed this check? – rds Mar 13 '15 at 17:23
  • `dayOfYear = DateFormat.format("YD", new Date())` This would fix the one year after use issue. This check can be run at any time, though I am not familiar with Android to know what is ideal. – rockerBOO Mar 13 '15 at 18:15
  • What type of object is dayOfYear? And how would I store lastResetDay? – freddiev4 Mar 14 '15 at 05:05
  • dayOfYear should be an INT. Not familiar with Android to where to store it, but where-ever the settings are usually stored – rockerBOO Mar 14 '15 at 10:29
2
  • Just save the last time the counter were reset in a pref.
  • When the fragment starts for the first time:
    • it checks the last time the counters were reset and maybe resets them
    • it registers an alarm via AlarmManager for the next midnight, which throws broadcast intent
    • it registers a broadcast receiver.
  • When the broadcast receiver receives the intent, it resets the counters, notifies the fragment and registers the alarm for the next midnight.
  • If the activity is stopped, remove your broadcast receiver and cancel your alarm, so that the app doesn't do work that wouldn't be visible anyway.
rds
  • 26,253
  • 19
  • 107
  • 134
1

Using AlarmManager for this task seems tedious work. Just add another variable to your SharedPreferences. Store the last time the counter was updated.(Probably - Calendar.getInstance().getTimeInMillis())

So when you open the fragment while getting the counter value, you have to get last time it was updated. Check the stored time against your current day. If it doesn't matches then reset the counter value.

If I did miss anything let me know..:)

Riyaz Ahamed
  • 802
  • 8
  • 14
  • You miss the fact that the screen can be on at 23:59:59 and the counters will stay unchanged the next second, when they should have been reset. Otherwise, I also agree, that there is a lot of overkill in having an AlarmManager, Service, BroadcastReceiver. – rds Mar 13 '15 at 17:25
-1

Some advises:

1.

COUNT_OOL = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("oolongCount", 0);
COUNT_WTE = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("whiteCount", 0);
COUNT_BLO = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("bloomingCount", 0);
COUNT_BLK = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("blackCount", 0);
COUNT_HBL = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("herbalCount", 0);
COUNT_GRN = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("greenCount", 0);
COUNT_MTE = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE).getInt("mateCount", 0);

should be edited like this

private SharedPreferences sharedPreferences;
sharedPreferences = getActivity().getSharedPreferences("keyname", Context.MODE_PRIVATE);
COUNT_OOL = sharedPreferences.getInt("oolongCount", 0);

etc

  1. Named in accordance with the needs of specification

  2. I think your code problem is to write more disorderly, finishing the first look at it, you first have a look, I now write about, by the way. Maybe you should use android system's clock.

gabriel
  • 2,351
  • 4
  • 20
  • 23
sundroid
  • 24
  • 3
  • it doesn't answer the question, even though you are completely right on what you say. – rds Mar 13 '15 at 17:27