package apt.tutorial.two; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.Binder; import android.os.IBinder; import android.os.SystemClock; import android.util.Log; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import winterwell.jtwitter.Twitter; import apt.tutorial.IPostListener; import apt.tutorial.IPostMonitor; import com.commonsware.cwac.wakeful.WakefulIntentService; public class PostMonitor extends WakefulIntentService { public static final int NOTIFICATION_ID=1337; public static final String STATUS_UPDATE="apt.tutorial.three.STATUS_UPDATE"; public static final String FRIEND="apt.tutorial.three.FRIEND"; public static final String STATUS="apt.tutorial.three.STATUS"; public static final String CREATED_AT="apt.tutorial.three.CREATED_AT"; public static final String POLL_ACTION="apt.tutorial.three.POLL_ACTION"; private static final String NOTIFY_KEYWORD="snicklefritz"; private static final int INITIAL_POLL_PERIOD=1000; private static final int POLL_PERIOD=60000; private Set<Long> seenStatus=new HashSet<Long>(); private Map<IPostListener, Account> accounts= new ConcurrentHashMap<IPostListener, Account>(); private final Binder binder=new LocalBinder(); private AtomicBoolean isBatteryLow=new AtomicBoolean(false); private AlarmManager alarm=null; private PendingIntent pi=null; public PostMonitor() { super("PostMonitor"); } @Override public void onCreate() { super.onCreate(); registerReceiver(onBatteryChanged, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); alarm=(AlarmManager)getSystemService(Context.ALARM_SERVICE); Intent i=new Intent(this, OnAlarmReceiver.class); pi=PendingIntent.getBroadcast(this, 0, i, 0); setAlarm(INITIAL_POLL_PERIOD); } @Override public IBinder onBind(Intent intent) { return(binder); } @Override public void onDestroy() { super.onDestroy(); alarm.cancel(pi); unregisterReceiver(onBatteryChanged); } @Override protected void doWakefulWork(Intent i) { if (i.getAction().equals(POLL_ACTION)) { for (Account l : accounts.values()) { poll(l); } } setAlarm(isBatteryLow.get() ? POLL_PERIOD*10 : POLL_PERIOD); } private void setAlarm(long period) { alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+period, pi); } private void poll(Account l) { try { Twitter client=new Twitter(l.user, l.password); client.setAPIRootUrl("https://identi.ca/api"); List<Twitter.Status> timeline=client.getFriendsTimeline(); for (Twitter.Status s : timeline) { if (!seenStatus.contains(s.id)) { try { Intent broadcast=new Intent(STATUS_UPDATE); broadcast.putExtra(FRIEND, s.user.screenName); broadcast.putExtra(STATUS, s.text); broadcast.putExtra(CREATED_AT, s.createdAt.toString()); sendBroadcast(broadcast); } catch (Throwable t) { Log.e("PostMonitor", "Exception in callback", t); } seenStatus.add(s.id); if (s.text.indexOf(NOTIFY_KEYWORD)>-1) { showNotification(); } } } } catch (Throwable t) { android.util.Log.e("PostMonitor", "Exception in poll()", t); } } private void showNotification() { final NotificationManager mgr= (NotificationManager)getSystemService(NOTIFICATION_SERVICE); Notification note=new Notification(R.drawable.status, "New matching post!", System.currentTimeMillis()); Intent i=new Intent(this, Patchy.class); i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP| Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent pi=PendingIntent.getActivity(this, 0, i, 0); note.setLatestEventInfo(this, "Identi.ca Post!", "Found your keyword: "+NOTIFY_KEYWORD, pi); mgr.notify(NOTIFICATION_ID, note); } BroadcastReceiver onBatteryChanged=new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { int pct=100 *intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 1) /intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1); isBatteryLow.set(pct<=25); } }; class Account { String user=null; String password=null; IPostListener callback=null; Account(String user, String password, IPostListener callback) { this.user=user; this.password=password; this.callback=callback; } } public class LocalBinder extends Binder implements IPostMonitor { public void registerAccount(String user, String password, IPostListener callback) { Account l=new Account(user, password, callback); poll(l); accounts.put(callback, l); } public void removeAccount(IPostListener callback) { accounts.remove(callback); } } }