Skip to content

Instantly share code, notes, and snippets.

@creativetrendsapps
Last active June 12, 2016 17:29
Show Gist options
  • Save creativetrendsapps/da211dd75cfb4af4cb79 to your computer and use it in GitHub Desktop.
Save creativetrendsapps/da211dd75cfb4af4cb79 to your computer and use it in GitHub Desktop.
Notifications with messages
package com.creativtrendz.folio.notifications;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.widget.Toast;
import com.creativetrends.folio.app.key.R;
import com.creativtrendz.folio.activities.FolioApplication;
import com.creativtrendz.folio.activities.MainActivity;
import com.creativtrendz.folio.services.Connectivity;
import com.creativtrendz.folio.utils.Logger;
import net.grandcentrix.tray.TrayAppPreferences;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
public class FolioNotifications extends Service {
private static final String BASE_URL = "https://mobile.facebook.com";
private static final String NOTIFICATIONS_URL = "https://m.facebook.com/notifications.php";
private static final String NOTIFICATIONS_URL_BACKUP = "https://mobile.facebook.com/notifications.php";
private static final String MESSAGES_URL = "https://m.facebook.com/messages";
private static final String MESSAGES_URL_BACKUP = "https://mobile.facebook.com/messages";
private static String NOTIFICATION_MESSAGE_URL = "https://m.facebook.com/messages#";
private Context context = this;
private static final int MAX_RETRY = 3;
private static final int JSOUP_TIMEOUT = 10000;
private static final String TAG;
private final HandlerThread handlerThread;
private final Handler handler;
private static Runnable runnable;
private volatile boolean shouldContinue = true;
private static String userAgent;
private TrayAppPreferences trayPreferences;
private final Logger Log;
static {
TAG = FolioNotifications.class.getSimpleName();
}
public FolioNotifications() {
handlerThread = new HandlerThread("Handler Thread");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
Log = Logger.getInstance();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
Log.i(TAG, "********** Service created! **********");
super.onCreate();
trayPreferences = new TrayAppPreferences(getApplicationContext());
runnable = new HandlerRunnable();
handler.postDelayed(runnable, 3000);
long now = System.currentTimeMillis();
long sinceLastCheck = now - trayPreferences.getLong("last_check", now);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: Service stopping...");
super.onDestroy();
synchronized (handler) {
shouldContinue = false;
handler.notify();
}
handler.removeCallbacksAndMessages(null);
handlerThread.quit();
}
/** A runnable used by the Handler to schedule checking. */
private class HandlerRunnable implements Runnable {
public void run() {
try {
final int timeInterval = trayPreferences.getInt("interval_pref", 1800000);
Log.i(TAG, "Time interval: " + (timeInterval / 1000) + " seconds");
final long now = System.currentTimeMillis();
final long sinceLastCheck = now - trayPreferences.getLong("last_check", now);
final boolean ntfLastStatus = trayPreferences.getBoolean("ntf_last_status", false);
final boolean msgLastStatus = trayPreferences.getBoolean("msg_last_status", false);
if ((sinceLastCheck < timeInterval) && ntfLastStatus && msgLastStatus) {
final long waitTime = timeInterval - sinceLastCheck;
if (waitTime >= 1000) {
Log.i(TAG, "I'm going to wait. Resuming in: " + (waitTime / 1000) + " seconds");
synchronized (handler) {
try {
handler.wait(waitTime);
} catch (InterruptedException ex) {
Log.i(TAG, "Thread interrupted");
} finally {
Log.i(TAG, "Lock is now released");
}
}
}
}
if (shouldContinue) {
if (Connectivity.isConnected(getApplicationContext())) {
Log.i(TAG, "Internet connection active. Starting AsyncTasks...");
String connectionType = "Wi-Fi";
if (Connectivity.isConnectedMobile(getApplicationContext()))
connectionType = "Mobile";
Log.i(TAG, "Connection Type: " + connectionType);
userAgent = trayPreferences.getString("webview_user_agent", System.getProperty("http.agent"));
Log.i(TAG, "User Agent: " + userAgent);
if (trayPreferences.getBoolean("notifications_activated", false))
new CheckNotificationsTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
if (trayPreferences.getBoolean("messages_activated", false))
new CheckMessagesTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
trayPreferences.put("last_check", System.currentTimeMillis());
} else
Log.i(TAG, "No internet connection. Skip checking.");
handler.postDelayed(runnable, timeInterval);
} else
Log.i(TAG, "Notified to stop running. Exiting...");
} catch (RuntimeException re) {
Log.i(TAG, "RuntimeException caught");
restartItself();
}
}
}
/** Notifications checker task: it checks Facebook notifications only. */
private class CheckNotificationsTask extends AsyncTask<Void, Void, Element> {
boolean syncProblemOccurred = false;
private Element getElement(String connectUrl) {
try {
return Jsoup.connect(connectUrl).userAgent(userAgent).timeout(JSOUP_TIMEOUT)
.cookie("https://mobile.facebook.com", CookieManager.getInstance().getCookie("https://mobile.facebook.com")).get()
.select("a.touchable").not("a._19no").not("a.button").first();
} catch (IllegalArgumentException ex) {
Log.i("Facebook Notifications", "Cookie sync problem occurred");
if (!syncProblemOccurred) {
syncProblemToast();
syncProblemOccurred = true;
}
} catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
@Override
protected Element doInBackground(Void... params) {
Element result = null;
int tries = 0;
syncCookies();
while (tries++ < MAX_RETRY && result == null) {
Log.i("Facebook Notifications", "Trying sync " + tries);
Log.i("Facebook Notifications", "Trying: " + MESSAGES_URL);
Element notif = getElement(NOTIFICATIONS_URL);
if (notif != null) {
result = notif;
}
}
return result;
}
@Override
protected void onPostExecute(Element result) {
try {
if (result == null) {
return;
}
if (result.text() == null) {
return;
}
if (!trayPreferences.getBoolean("activity_visible", false) || trayPreferences.getBoolean("notifications_everywhere", true)) {
notifier(result.text(), BASE_URL + result.attr("href"), false);
Log.i("Facebook Notifications", "Starting sync");
}
trayPreferences.put("ntf_last_status", true);
Log.i("CheckMessagesTask", "onPostExecute: Aight biatch ;)");
} catch (NumberFormatException ex) {
trayPreferences.put("ntf_last_status", false);
Log.i("CheckMessagesTask", "onPostExecute: Failure");
}
}
}
/** Messages checker task: it checks new messages only. */
private class CheckMessagesTask extends AsyncTask<Void, Void, String> {
boolean syncProblemOccurred = false;
private String getNumber(String connectUrl) {
try {
Elements message = Jsoup.connect(connectUrl).userAgent(userAgent).timeout(JSOUP_TIMEOUT)
.cookie("https://m.facebook.com", CookieManager.getInstance().getCookie("https://m.facebook.com")).get()
.select("div#viewport").select("div#page").select("div._129-")
.select("#messages_jewel").select("span._59tg");
return message.html();
} catch (IllegalArgumentException ex) {
Log.i("CheckMessagesTask", "Cookie sync problem occurred");
if (!syncProblemOccurred) {
syncProblemToast();
syncProblemOccurred = true;
}
} catch (IOException ex) {
ex.printStackTrace();
}
return "failure";
}
@Override
protected String doInBackground(Void... params) {
String result = null;
int tries = 0;
syncCookies();
while (tries++ < MAX_RETRY && result == null) {
Log.i("CheckMessagesTask", "doInBackground: Processing... Trial: " + tries);
Log.i("CheckMsgTask:getNumber", "Trying: " + MESSAGES_URL);
String number = getNumber(MESSAGES_URL);
if (!number.matches("^[+-]?\\d+$")) {
Log.i("CheckMsgTask:getNumber", "Trying: " + MESSAGES_URL_BACKUP);
number = getNumber(MESSAGES_URL_BACKUP);
}
if (number.matches("^[+-]?\\d+$"))
result = number;
}
return result;
}
@SuppressLint("StringFormatInvalid")
@Override
protected void onPostExecute(String result) {
try {
int newMessages = Integer.parseInt(result);
if (!trayPreferences.getBoolean("activity_visible", false) || trayPreferences.getBoolean("notifications_everywhere", true)) {
if (newMessages == 1)
notifier(getString(R.string.you_have_one_message), NOTIFICATION_MESSAGE_URL, true);
else if (newMessages > 1)
notifier(String.format(getString(R.string.you_have_n_messages), newMessages), NOTIFICATION_MESSAGE_URL, true);
}
trayPreferences.put("msg_last_status", true);
Log.i("CheckMessagesTask", "onPostExecute: Aight biatch ;)");
} catch (NumberFormatException ex) {
trayPreferences.put("msg_last_status", false);
Log.i("CheckMessagesTask", "onPostExecute: Failure");
}
}
}
/** CookieSyncManager was deprecated in API level 21.
* We need it for API level lower than 21 though.
* In API level >= 21 it's done automatically.
*/
@SuppressWarnings("deprecation")
private void syncCookies() {
if (Build.VERSION.SDK_INT < 21) {
CookieSyncManager.createInstance(getApplicationContext());
CookieSyncManager.getInstance().sync();
}
}
private void syncProblemToast() {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), getString(R.string.sync_problem),
Toast.LENGTH_SHORT).show();
}
});
}
private void restartItself() {
final Context context = FolioApplication.getContextOfApplication();
final Intent intent = new Intent(context, FolioNotifications.class);
context.stopService(intent);
context.startService(intent);
}
@SuppressWarnings("deprecation")
@SuppressLint("InlinedApi")
private void notifier(String notificationText, String url, boolean isMessage) {
Log.i(TAG, "Start notification - isMessage: " + isMessage);
@SuppressWarnings("deprecation")
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentTitle(getResources().getString(R.string.app_name));
mBuilder.setContentText(notificationText);
mBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(notificationText));
mBuilder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher));
mBuilder.setColor(getResources().getColor(R.color.PrimaryDarkColor));
mBuilder.setTicker(notificationText);
mBuilder.setWhen(System.currentTimeMillis());
String ringtoneKey = "ringtone";
if (isMessage)
ringtoneKey = "ringtone_msg";
Uri ringtoneUri = Uri.parse(trayPreferences.getString(ringtoneKey, "content://settings/system/notification_sound"));
mBuilder.setSound(ringtoneUri);
if (trayPreferences.getBoolean("vibrate", false))
mBuilder.setVibrate(new long[]{500, 500});
else
mBuilder.setVibrate(new long[]{0L});
if (trayPreferences.getBoolean("led_light", false)) {
Resources resources = getResources(), systemResources = Resources.getSystem();
mBuilder.setLights(Color.CYAN,
resources.getInteger(systemResources.getIdentifier("config_defaultNotificationLedOn", "integer", "android")),
resources.getInteger(systemResources.getIdentifier("config_defaultNotificationLedOff", "integer", "android")));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
mBuilder.setPriority(Notification.PRIORITY_HIGH);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (!isMessage) {
Intent actionIntent = new Intent(this, MainActivity.class);
actionIntent.putExtra("start_url", url);
actionIntent.setAction("NOTIFICATION_URL_ACTION");
PendingIntent pendingMessageIntent = PendingIntent.getActivity(this, 0, actionIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setSmallIcon(R.drawable.ic_public);
mBuilder.setColor(getResources().getColor(R.color.PrimaryDarkColor));
mBuilder.setContentIntent(pendingMessageIntent);
mBuilder.setOngoing(false);
Notification notification = mBuilder.build();
notificationManager.notify(0, notification);
} else {
Intent messageIntent = new Intent(this, MainActivity.class);
messageIntent.putExtra("start_url", NOTIFICATION_MESSAGE_URL);
PendingIntent pendingMessageIntent = PendingIntent.getActivity(this, 1, messageIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setSmallIcon(R.drawable.ic_messenger_new);
mBuilder.setColor(getResources().getColor(R.color.PrimaryDarkColor));
mBuilder.setContentIntent(pendingMessageIntent);
mBuilder.setOngoing(false);
Notification message = mBuilder.build();
notificationManager.notify(1, message);
}
}
public static void clearNotifications() {
NotificationManager notificationManager = (NotificationManager)
FolioApplication.getContextOfApplication().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(0);
}
public static void clearMessages() {
NotificationManager notificationManager = (NotificationManager)
FolioApplication.getContextOfApplication().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment