package com.steps.geosms.notifications;
import android.app.Activity;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Vibrator;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.text.Html;
import android.util.Log;
import android.util.Pair;
import com.ioane.sharvadze.geosms.R;
import com.steps.geosms.MyPreferencesManager;
import com.steps.geosms.conversation.ConversationActivity;
import com.steps.geosms.conversationsList.ConversationsListActivity;
import com.steps.geosms.objects.Contact;
import com.steps.geosms.objects.Conversation;
import com.steps.geosms.objects.SMS;
import com.steps.geosms.utils.Constants;
import com.steps.geosms.utils.Utils;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* Created by ioane on 6/9/15.
*/
public class SmsManagerService extends IntentService {
private static final String TAG = SmsManagerService.class.getSimpleName();
private static final int VIBRATE_LENGTH = 100;
private static final String GROUP = "GEO_SMS_NOTIF_GROUP";
/**
* No thread is shown to user.
*/
public static final int THREAD_ID_NONE = -123123;
private static final String IS_SUMMARY = "is_summary";
private static final int SUMMARY = -1000;
private static final String RECEIVED_TAG = "received";
private static final String FAILED_TAG = "failed";
private static AtomicInteger mIntentId;
/**
* Saves current shown threadId that user interacts with.
*/
private static long currentThreadId = THREAD_ID_NONE;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
\ */
public SmsManagerService() {
super("sms_manager");
}
public static void updateThreadId(long threadId){
currentThreadId = threadId;
}
private static boolean mIsNotificationsOn = true;
private int mResultCode;
@Override
protected void onHandleIntent(Intent intent) {
handle(getApplicationContext(),intent);
SmsReceiver.completeWakefulIntent(intent);
}
void handle(Context context,Intent intent){
mResultCode = intent.getIntExtra("result",-1);
String action = intent.getAction();
Log.i(TAG, "handle : " + action);
// New versions only listen to SMS_DELIVER.
if(Build.VERSION.SDK_INT >= 19 && action.contains(Constants.Actions.SMS_RECEIVED_OLD)){
Log.i(TAG, "action dismissed " + action);
return;
}
mIsNotificationsOn = MyPreferencesManager.isNotificationOn(context);
if(action.equals(Constants.Actions.SMS_DELIVERED)){
handleSmsDelivered(context, intent);
}else if(action.equals(Constants.Actions.MESSAGE_SENT)){
handleSmsSend(context, intent);
}else if(action.contains(Constants.Actions.SMS_RECEIVED_NEW) ||
action.contains(Constants.Actions.SMS_RECEIVED_OLD)){
handleSmsReceive(context, intent);
}else if(action.contains(Constants.Actions.RECEIVED_NOTIFICATION_DISMISSED)){
handleDismissedNotifications(intent);
}else{
Log.w(TAG,"unknown action "+action);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSubText(action);
mNotificationManager.notify(1212112, builder.build());
}
}
private void handleSmsReceive(Context ctx,Intent intent){
Bundle bundle = intent.getExtras();
if(bundle == null){
Log.w(TAG,"bundle is empty");
return;
}
ContentValues values = SMS.getContentValuesFromBundle(bundle);
if(values == null)
return;
values.put(Constants.MESSAGE.DATE,System.currentTimeMillis()); // time correction
Uri smsUri = ctx.getContentResolver().insert(Uri.parse("content://sms/"), values);
String address = values.getAsString(Constants.ADDRESS);
Contact contact = new Contact(ctx,address);
long threadId = Conversation.getOrCreateThreadId(ctx, contact.getAddress());
SMS sms = new SMS(values);
sms.setId(Long.parseLong((smsUri.getLastPathSegment())));
NotificationHelper notificationHelper = new NotificationHelper(ctx);
if(threadId != currentThreadId) {
notificationHelper.insertSMS(sms.getId(),threadId,0);
sendNotification(notificationHelper.getAllReceivedNotifications(), true);
}else {
// if user is on this chat , make no notification , just slight vibration
values = new ContentValues();
values.put(Constants.MESSAGE.READ, 1); // is read
// update message as read.
ctx.getContentResolver().update(smsUri, values,null,null);
Vibrator v = (Vibrator) ctx.getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(VIBRATE_LENGTH);
}
}
private void handleSmsDelivered(Context ctx, Intent intent){
Uri deliveredSmsUri = intent.getData();
if(deliveredSmsUri == null){
Log.w(TAG, "deliveredSmsUri is null");
return;
}
ContentValues values = new ContentValues();
values.put(Constants.MESSAGE.STATUS, Constants.MESSAGE.STATUS_COMPLETE);
try{
ctx.getContentResolver().update(deliveredSmsUri,values,null,null);
}catch (Exception e){
Log.e(TAG,"exception entering delivery report");
e.printStackTrace();
}
}
private void handleSmsSend(Context ctx, final Intent intent){
Uri pendingSmsUri = intent.getData();
if(pendingSmsUri == null){
Log.w(TAG,"pendingSmsURI is null");
return;
}
Log.i(TAG, pendingSmsUri.toString());
ContentValues values = new ContentValues();
long smsId = Long.parseLong(pendingSmsUri.getLastPathSegment());
String address = intent.getStringExtra(Constants.ADDRESS);
long threadId = intent.getLongExtra(Constants.THREAD_ID, 0);
switch (mResultCode){
case Activity.RESULT_OK:
Log.d(TAG, "SMS sent");
values.put(Constants.MESSAGE.TYPE,Constants.MESSAGE.MESSAGE_TYPE_SENT);
break;
default:
values.put(Constants.MESSAGE.TYPE,Constants.MESSAGE.MESSAGE_TYPE_FAILED);
if(mIsNotificationsOn){
NotificationManager notificationManager =
(NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
if(address == null){
Log.e(TAG,"address was null, SMS_FAILED");
}
if(currentThreadId != threadId){
NotificationHelper helper = new NotificationHelper(ctx);
helper.insertSMS(smsId,threadId,1);
sendNotification(helper.getAllFailedNotifications(),false);
}
}
Log.d(TAG,"Message sending failed result Code :" + mResultCode);
}
try{
ctx.getContentResolver().update(pendingSmsUri,values,null,null);
}catch (Exception e){
Log.w(TAG, "couldn't update sms! some bug here");
}
}
public static void dismissThreadNotifications(Context context,long threadId){
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(RECEIVED_TAG,(int) threadId); // delete all this thread messages.
notificationManager.cancel(FAILED_TAG,(int) threadId); // delete all this thread messages.
NotificationHelper helper = new NotificationHelper(context);
helper.clearAllThreadNotifications(threadId);
// if summary notifications were also cleared, clear them too.
if(helper.getAllReceivedNotifications() == null){
notificationManager.cancel(RECEIVED_TAG,SUMMARY);
}
if(helper.getAllFailedNotifications() == null){
notificationManager.cancel(FAILED_TAG,SUMMARY);
}
helper.cleanUp();
}
public static void dismissSummaryNotifications(Context context){
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(RECEIVED_TAG,SUMMARY);
notificationManager.cancel(FAILED_TAG, SUMMARY);
}
public void handleDismissedNotifications(Intent intent){
boolean isReceivedType = intent.getIntExtra(Constants.NOTIFICATION_TYPE,0) == 0;
boolean isSummary = intent.getBooleanExtra(IS_SUMMARY,false);
NotificationHelper helper = new NotificationHelper(getApplicationContext());
if(isReceivedType){
if(isSummary){
helper.clearAllReceivedNotifications();
}else{
long threadId = intent.getLongExtra(Constants.THREAD_ID, 0);
helper.clearThreadReceivedNotifications(threadId);
}
}else {
if(isSummary){
helper.clearAllFailedNotifications();
}else{
long threadId = intent.getLongExtra(Constants.THREAD_ID, 0);
helper.clearThreadFailedNotifications(threadId);
}
}
helper.cleanUp();
}
/**
* @param notifications list of notification entries
* @param isReceivedType if true means that notifications is for received sms.
* if false - notification is for failed sms.
*/
private void sendNotification(List<Pair<Long, Long>> notifications,boolean isReceivedType){
if(notifications.size() <= 0)
return;
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext());
Uri uri = Uri.parse("content://sms/");
ContentResolver cv = getContentResolver();
if(notifications.size() > 1){
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
for(Pair<Long,Long> notif : notifications){
Cursor c = cv.query(uri, null, "_id=?", new String[]{"" + notif.first}, null);
if(c == null)
continue;
if(!c.moveToFirst()){
c.close();
Log.i(TAG,"returns null");
continue;
}
String address = c.getString(c.getColumnIndex("address"));
String text = c.getString(c.getColumnIndex("body"));
Contact contact = new Contact(getBaseContext(),address);
inboxStyle.addLine(Html.fromHtml(String.format("%s : %s", "<b>"+ contact.getDisplayName() + "</b>", text)));
}
Bitmap photo = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_sms_received);
String formatContentText = isReceivedType? getString(R.string.unread_sms_content_format) :
getString(R.string.failed_sms_content_format);
mBuilder.setStyle(inboxStyle)
.setLargeIcon(photo)
.setSmallIcon(isReceivedType? R.mipmap.ic_sms_received :
R.mipmap.ic_sms_failed)
.setContentTitle(isReceivedType? getText(R.string.geosms_unread_sms) :
getText(R.string.geosms_failed_sms))
.setContentText(String.format(formatContentText, notifications.size()))
.setDefaults(Notification.DEFAULT_ALL)
.setAutoCancel(true)
.setGroup(GROUP)
.setColor(Color.argb(100,20,80,200))
.setGroupSummary(true);
Intent resultIntent = new Intent(this, ConversationsListActivity.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(ConversationsListActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
Intent dismissedPI = new Intent(Constants.Actions.RECEIVED_NOTIFICATION_DISMISSED,
null , this, SmsReceiver.class);
dismissedPI.putExtra(IS_SUMMARY, true);
//dismissedPI.putExtra(Constants.NOTIFICATION_ARRAY, notifications.toArray());
dismissedPI.putExtra(Constants.NOTIFICATION_TYPE, isReceivedType ? 0 : 1);
int uniqueInt = new Random().nextInt();
mBuilder.setDeleteIntent(PendingIntent.getBroadcast(this, uniqueInt, dismissedPI, PendingIntent.FLAG_CANCEL_CURRENT));
notificationManager.notify(isReceivedType? RECEIVED_TAG : FAILED_TAG,
SUMMARY, mBuilder.build());
}else {
Cursor c = cv.query(uri, null, "_id=?", new String[]{"" + notifications.get(0).first}, null);
if(c == null)
return;
if(!c.moveToFirst()){
c.close();
return;
}
String address = c.getString(c.getColumnIndex("address"));
String text = c.getString(c.getColumnIndex("body"));
Contact contact = new Contact(getBaseContext(),address);
contact.resolveContactImage(getApplicationContext(),100);
Bitmap photo = contact.getPhoto();
mBuilder.setLargeIcon(photo)
.setSmallIcon(isReceivedType? R.mipmap.ic_sms_received : R.mipmap.ic_sms_failed)
.setContentTitle(isReceivedType? contact.getDisplayName() :
getString(R.string.sms_sending_faild))
.setDefaults(Notification.DEFAULT_ALL)
.setContentText(text)
.setStyle(new NotificationCompat.BigTextStyle().bigText(text))
.setAutoCancel(true)
.setGroup(GROUP);
// now build callback
Intent resultIntent = new Intent(this, ConversationActivity.class);
ArrayList<Contact> data = new ArrayList<>(1);
data.add(contact);
resultIntent.putExtra(Constants.CONTACT_DATA, data);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(ConversationActivity.class);
stackBuilder.addNextIntent(resultIntent);
int uniqueInt = new Random().nextInt();
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
uniqueInt,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
Intent dismissedPI = new Intent(Constants.Actions.RECEIVED_NOTIFICATION_DISMISSED,
null , this, SmsReceiver.class);
long threadId = notifications.get(0).second;
dismissedPI.putExtra(IS_SUMMARY, true);
dismissedPI.putExtra(Constants.NOTIFICATION_TYPE, isReceivedType? 0 : 1);
dismissedPI.putExtra(Constants.THREAD_ID,threadId);
Log.i(TAG,"thread_id " + threadId);
uniqueInt = new Random().nextInt();
mBuilder.setDeleteIntent(PendingIntent.getBroadcast(this, uniqueInt, dismissedPI, PendingIntent.FLAG_CANCEL_CURRENT));
notificationManager.notify(isReceivedType? RECEIVED_TAG : FAILED_TAG,
(int)threadId,mBuilder.build());
}
}
}