john
john

Reputation: 707

Android: How to check email at a specific time with AlarmManager?

I need to print all my incoming email at 9AM every morning. Here is what I tried

The email part works fine. I am able to connect to email and print them all. The problem comes when I introduce the Alarm Manager. When I run it, nothing happens. It just hangs

MainActivity.java

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Properties; 
import javax.mail.*;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public void checkEmail(View view) {
        Log.d("test", "accessing gmail");

        Calendar cur_cal = new GregorianCalendar();
        cur_cal.setTimeInMillis(System.currentTimeMillis());//set the current time and date for this calendar

        Calendar cal = new GregorianCalendar();
        cal.add(Calendar.DAY_OF_YEAR, cur_cal.get(Calendar.DAY_OF_YEAR));
        cal.set(Calendar.HOUR_OF_DAY, 9);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, cur_cal.get(Calendar.SECOND));
        cal.set(Calendar.MILLISECOND, cur_cal.get(Calendar.MILLISECOND));
        cal.set(Calendar.DATE, cur_cal.get(Calendar.DATE));
        cal.set(Calendar.MONTH, cur_cal.get(Calendar.MONTH));


        // create an Intent 
        Intent intentAlarm = new Intent(this, AlarmReceiver.class);
        PendingIntent pi = PendingIntent.getService(this, 0, intentAlarm, 0);

        // create the object
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        //set the alarm for 9 AM 
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 1000 * 5, pi);

        Toast.makeText(this, "Alarm Scheduled for 9 AM", Toast.LENGTH_LONG).show();

    }


}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.admin.myapplication.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:textSize="24sp"
        android:layout_height="wrap_content"
        android:text="Email Checker"
        android:id="@+id/textView2" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Button"
        android:id="@+id/button"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="152dp"
        android:onClick="checkEmail"/>


</RelativeLayout>

AlarmReceiver.java (UPDATED CLASS)

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



public class AlarmReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
         Intent i = new Intent(context, AsyncTaskActvity.class);
    i.putExtra("foo", "bar");
    context.startService(i);
        Log.d("test", "async");
        Toast.makeText(context, "Alarm Triggered", Toast.LENGTH_LONG).show();
    }

}

AsyncTaskActvity.java

package com.example.admin.myapplication;

import android.os.AsyncTask;
import android.util.Log;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Store;

public class AsyncTaskActvity extends AsyncTask {


    @Override
    protected Object doInBackground(Object[] params) {

        computerUp();

        return null;


    }

    protected boolean computerUp () {



        try {

            Properties props = java.lang.System.getProperties();
            props.setProperty("mail.store.protocol", "imaps");
            Session session = Session.getDefaultInstance(props, null);
            Store store = session.getStore("imaps");
            store.connect("imap.gmail.com", "[email protected]", "mypassword");
            Session s = Session.getInstance(props);


            Message[] msgs;


            Folder inbox = store.getFolder("Inbox");
            inbox.open(Folder.READ_ONLY);
            msgs = inbox.getMessages();

            for (int i = 0; i < msgs.length; i++) {
                msgs[i].getSubject();
                Log.d("test", msgs[i].getSubject());
            }

        } catch (Exception e) {
            Log.e(e.getClass().getName(), e.getMessage(), e);
        }

        return true;
    }


} 

When I run it, nothing happens. What am I doing wrong?

Upvotes: 9

Views: 487

Answers (3)

Vyacheslav
Vyacheslav

Reputation: 27221

First of all, if you want to launch some code inside Android device you have to use this:

  1. AlarmManager which launches BroadcastReceiver
  2. use WakeupBroadcastReceiver to wake up your device.
  3. Use stay-in-wakeup state of Android device while executing the code.
  4. Do not forget to recharge after restarting your device.
  5. YOU HAVE A PROBLEM WHILE STARTING ASYNCTASK !!!
  6. You have incorrect alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 1000 * 5, pi); implementation. this string mean:"execute my code each 5 seconds. Start it is now!". This is not 9AM time;)

So, try this:

  1. To charge AlarmManager at specific time:
Intent intent = new Intent(BRWakeup.INTENT_FILTER) ;

        PendingIntent pi =  PendingIntent.getBroadcast(ctx, intentalarm, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        int currentapiVersion = android.os.Build.VERSION.SDK_INT;
        if (currentapiVersion < android.os.Build.VERSION_CODES.KITKAT){
          am.set(wakeup?AlarmManager.RTC_WAKEUP:AlarmManager.RTC, nexttime, pi);
        } else if (currentapiVersion >= android.os.Build.VERSION_CODES.LOLLIPOP) 
          if (currentapiVersion < android.os.Build.VERSION_CODES.M) {
              am.setExact(AlarmManager.RTC_WAKEUP, nexttime, pi);
          } else {
                  am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nexttime, pi);
          }
        }

where ctx - current Context class (parent Class for Activity and Service). For example, in Activity class this is getContext() or getActivity(). nexttime - time to lauch the receiver. For example, long nexttime = System.getCurrentTimeMillis();, AlarmManager am = (AlarmManager) ctx.getSystemService(Activity.ALARM_SERVICE); , intentalarm - is a some unique number to detect this launch event in future. It may be any number your want but the constant in your app. For example, final int intentalarm = 22344;

  1. To launch service from BroadcastReceiver:
public class BRWakeup extends WakefulBroadcastReceiver {
  public static final String INTENT_FILTER = "com.example.BRWakeup";
  @Override
  public void onReceive(Context ctx, Intent intent) {
      // TODO Auto-generated method stub
      OWakeLocker.acquire(ctx, 0);
      ComponentName comp = new ComponentName(ctx.getPackageName(),
                SService.class.getName());
      startWakefulService(ctx, intent.setComponent(comp));
  }

}
  1. OWakeLocker is my own class which stores all launched services to save their lifes.
public class OWakeLocker {
    private static PowerManager.WakeLock[] wakeLocks = new PowerManager.WakeLock[1];//Services count

    @SuppressWarnings("deprecation")
  public static void acquire(Context ctx, int index) {
      WakeLock wakeLock = wakeLocks[index];
        if (wakeLock != null) wakeLock.release();

        PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
          wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
                  PowerManager.ACQUIRE_CAUSES_WAKEUP |
                  PowerManager.ON_AFTER_RELEASE, _.APPNAME + Integer.toString(index));
        if (wakeLock != null && wakeLock.isHeld()){
          wakeLock.acquire();
        }
    }

    public static void release(int index) {
      WakeLock wakeLock = wakeLocks[index];
        if (wakeLock != null) 
          wakeLock.release();
          wakeLock = null;
    }
}
  1. Use this service to execute your code:
public class SService extends Service {

@Override
    public void onCreate() {
      OWakeLocker.acquire(this, 0);
      super.onCreate();
}
@Override
  public void onDestroy() {
      super.onDestroy();
      OWakeLocker.release(_.indexNOTS_TAT);
  }
@Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      //your code to start AsyncTask

AsyncTaskActvity atask = new AsyncTaskActvity();
    atsk.execute();
}
}
  1. To calculate good 9AM time you have to use this code: time and date for this calendar
     Calendar cal = Calendar.getInstance();
boolean iftoday = cal.get(Calendar.HOUR_OF_DAY) < 9;

//to set the time as exact
        cal.set(Calendar.HOUR_OF_DAY, 9);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        if (!iftoday) {
           cal.add(Calendar.DATE, 1);  
        }
        long nexttime = cal.getTimeInMillis();//this is the correct next time.
  1. Use setExactAndAllowWhileIdle instead of setRepeating... . And recharge AlarmManager at SService.onDestroy() method using the same code as in the Activity

  2. Edit AndroidManifest.xml

<receiver android:name="com.example.BRWakeup"
          android:exported="false">
          <intent-filter>
              <action android:name="com.example.BRWakeup"/>
          </intent-filter>
      </receiver>
<service android:name="com.example.SService"></service>

Upvotes: 5

Anggrayudi H
Anggrayudi H

Reputation: 15165

First of all, change your code in the checkEmail() method to:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void checkEmail(View view) {
    Log.d("test", "accessing gmail");
    //set the current time and date for this calendar
    Calendar cal = new GregorianCalendar();
    cal.add(Calendar.DAY_OF_YEAR, 1);
    cal.set(Calendar.HOUR_OF_DAY, 9);
    cal.set(Calendar.MINUTE, 0);

    // create an Intent
    Intent intentAlarm = new Intent(this, AlarmReceiver.class);
    PendingIntent pi = PendingIntent.getBroadcast(this, 0, intentAlarm, 0);

    // create the object
    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

    //set the alarm for 9 AM 
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi);

    Toast.makeText(this, "Alarm Scheduled for 9 AM", Toast.LENGTH_LONG).show();
}

In AlarmReceiver.java class (make sure that this receiver already registered in manifest with custom action in intent-filter tag):

@Override
public void onReceive(Context context, Intent intent) {
     new AsyncTaskActvity().execute();
     Log.d("test", "async");
     Toast.makeText(context, "Alarm Triggered", Toast.LENGTH_LONG).show();
}

The main problem why you get the error is, you called startService() to a class that does not extend Service, i.e. AsyncTaskActvity. You need to find the difference between Service and AsyncTask.

Upvotes: 3

Chirag Arora
Chirag Arora

Reputation: 816

Use Intent-Service with Broadcast Receiver.

Follow below link with example :

Background Service

  public class MyAlarmReceiver extends BroadcastReceiver {
   public static final int REQUEST_CODE = 12345;
   public static final String ACTION = "com.codepath.example.servicesdemo.alarm";
 // Triggered by the Alarm periodically (starts the service to run task)
    @Override
    public void onReceive(Context context, Intent intent) {
     Intent i = new Intent(context, MyTestService.class);
     i.putExtra("foo", "bar");
     context.startService(i);
    }
}

Upvotes: 3

Related Questions