1 /* 2 * Copyright (C) 2007-2008 Esmertec AG. 3 * Copyright (C) 2007-2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.mms.ui; 19 20 import android.app.Activity; 21 import android.app.AlertDialog; 22 import android.content.ContentResolver; 23 import android.content.ContentUris; 24 import android.content.ContentValues; 25 import android.content.DialogInterface; 26 import android.content.DialogInterface.OnClickListener; 27 import android.database.Cursor; 28 import android.database.sqlite.SqliteWrapper; 29 import android.net.Uri; 30 import android.os.Bundle; 31 import android.os.Handler; 32 import android.os.Message; 33 import android.os.SystemClock; 34 import android.provider.Telephony.Sms; 35 import android.provider.Telephony.Sms.Inbox; 36 import android.telephony.SmsMessage; 37 import android.text.TextUtils; 38 import android.util.Log; 39 import android.view.Window; 40 41 import com.android.mms.R; 42 import com.android.mms.transaction.MessagingNotification; 43 44 /** 45 * Display a class-zero SMS message to the user. Wait for the user to dismiss 46 * it. 47 */ 48 public class ClassZeroActivity extends Activity { 49 private static final String BUFFER = " "; 50 private static final int BUFFER_OFFSET = BUFFER.length() * 2; 51 private static final String TAG = "display_00"; 52 private static final int ON_AUTO_SAVE = 1; 53 private static final String[] REPLACE_PROJECTION = new String[] { Sms._ID, 54 Sms.ADDRESS, Sms.PROTOCOL }; 55 private static final int REPLACE_COLUMN_ID = 0; 56 57 /** Default timer to dismiss the dialog. */ 58 private static final long DEFAULT_TIMER = 5 * 60 * 1000; 59 60 /** To remember the exact time when the timer should fire. */ 61 private static final String TIMER_FIRE = "timer_fire"; 62 63 private SmsMessage mMessage = null; 64 65 /** Is the message read. */ 66 private boolean mRead = false; 67 68 /** The timer to dismiss the dialog automatically. */ 69 private long mTimerSet = 0; 70 private AlertDialog mDialog = null; 71 72 private Handler mHandler = new Handler() { 73 @Override 74 public void handleMessage(Message msg) { 75 // Do not handle an invalid message. 76 if (msg.what == ON_AUTO_SAVE) { 77 mRead = false; 78 mDialog.dismiss(); 79 saveMessage(); 80 finish(); 81 } 82 } 83 }; 84 saveMessage()85 private void saveMessage() { 86 Uri messageUri = null; 87 if (mMessage.isReplace()) { 88 messageUri = replaceMessage(mMessage); 89 } else { 90 messageUri = storeMessage(mMessage); 91 } 92 if (!mRead && messageUri != null) { 93 MessagingNotification.nonBlockingUpdateNewMessageIndicator( 94 this, 95 MessagingNotification.THREAD_ALL, // always notify on class-zero msgs 96 false); 97 } 98 } 99 100 @Override onCreate(Bundle icicle)101 protected void onCreate(Bundle icicle) { 102 super.onCreate(icicle); 103 requestWindowFeature(Window.FEATURE_NO_TITLE); 104 getWindow().setBackgroundDrawableResource( 105 R.drawable.class_zero_background); 106 107 byte[] pdu = getIntent().getByteArrayExtra("pdu"); 108 String format = getIntent().getStringExtra("format"); 109 mMessage = SmsMessage.createFromPdu(pdu, format); 110 CharSequence messageChars = mMessage.getMessageBody(); 111 String message = messageChars.toString(); 112 if (TextUtils.isEmpty(message)) { 113 finish(); 114 return; 115 } 116 // TODO: The following line adds an emptry string before and after a message. 117 // This is not the correct way to layout a message. This is more of a hack 118 // to work-around a bug in AlertDialog. This needs to be fixed later when 119 // Android fixes the bug in AlertDialog. 120 if (message.length() < BUFFER_OFFSET) messageChars = BUFFER + message + BUFFER; 121 long now = SystemClock.uptimeMillis(); 122 mDialog = new AlertDialog.Builder(this).setMessage(messageChars) 123 .setPositiveButton(R.string.save, mSaveListener) 124 .setNegativeButton(android.R.string.cancel, mCancelListener) 125 .setCancelable(false).show(); 126 mTimerSet = now + DEFAULT_TIMER; 127 if (icicle != null) { 128 mTimerSet = icicle.getLong(TIMER_FIRE, mTimerSet); 129 } 130 } 131 132 @Override onStart()133 protected void onStart() { 134 super.onStart(); 135 long now = SystemClock.uptimeMillis(); 136 if (mTimerSet <= now) { 137 // Save the message if the timer already expired. 138 mHandler.sendEmptyMessage(ON_AUTO_SAVE); 139 } else { 140 mHandler.sendEmptyMessageAtTime(ON_AUTO_SAVE, mTimerSet); 141 if (false) { 142 Log.d(TAG, "onRestart time = " + Long.toString(mTimerSet) + " " 143 + this.toString()); 144 } 145 } 146 } 147 148 @Override onSaveInstanceState(Bundle outState)149 protected void onSaveInstanceState(Bundle outState) { 150 super.onSaveInstanceState(outState); 151 outState.putLong(TIMER_FIRE, mTimerSet); 152 if (false) { 153 Log.d(TAG, "onSaveInstanceState time = " + Long.toString(mTimerSet) 154 + " " + this.toString()); 155 } 156 } 157 158 @Override onStop()159 protected void onStop() { 160 super.onStop(); 161 mHandler.removeMessages(ON_AUTO_SAVE); 162 if (false) { 163 Log.d(TAG, "onStop time = " + Long.toString(mTimerSet) 164 + " " + this.toString()); 165 } 166 } 167 168 private final OnClickListener mCancelListener = new OnClickListener() { 169 public void onClick(DialogInterface dialog, int whichButton) { 170 dialog.dismiss(); 171 finish(); 172 } 173 }; 174 175 private final OnClickListener mSaveListener = new OnClickListener() { 176 public void onClick(DialogInterface dialog, int whichButton) { 177 mRead = true; 178 saveMessage(); 179 dialog.dismiss(); 180 finish(); 181 } 182 }; 183 extractContentValues(SmsMessage sms)184 private ContentValues extractContentValues(SmsMessage sms) { 185 // Store the message in the content provider. 186 ContentValues values = new ContentValues(); 187 188 values.put(Inbox.ADDRESS, sms.getDisplayOriginatingAddress()); 189 190 // Use now for the timestamp to avoid confusion with clock 191 // drift between the handset and the SMSC. 192 values.put(Inbox.DATE, new Long(System.currentTimeMillis())); 193 values.put(Inbox.PROTOCOL, sms.getProtocolIdentifier()); 194 values.put(Inbox.READ, Integer.valueOf(mRead ? 1 : 0)); 195 values.put(Inbox.SEEN, Integer.valueOf(mRead ? 1 : 0)); 196 197 if (sms.getPseudoSubject().length() > 0) { 198 values.put(Inbox.SUBJECT, sms.getPseudoSubject()); 199 } 200 values.put(Inbox.REPLY_PATH_PRESENT, sms.isReplyPathPresent() ? 1 : 0); 201 values.put(Inbox.SERVICE_CENTER, sms.getServiceCenterAddress()); 202 return values; 203 } 204 replaceMessage(SmsMessage sms)205 private Uri replaceMessage(SmsMessage sms) { 206 ContentValues values = extractContentValues(sms); 207 208 values.put(Inbox.BODY, sms.getMessageBody()); 209 210 ContentResolver resolver = getContentResolver(); 211 String originatingAddress = sms.getOriginatingAddress(); 212 int protocolIdentifier = sms.getProtocolIdentifier(); 213 String selection = Sms.ADDRESS + " = ? AND " + Sms.PROTOCOL + " = ?"; 214 String[] selectionArgs = new String[] { originatingAddress, 215 Integer.toString(protocolIdentifier) }; 216 217 Cursor cursor = SqliteWrapper.query(this, resolver, Inbox.CONTENT_URI, 218 REPLACE_PROJECTION, selection, selectionArgs, null); 219 220 try { 221 if (cursor.moveToFirst()) { 222 long messageId = cursor.getLong(REPLACE_COLUMN_ID); 223 Uri messageUri = ContentUris.withAppendedId( 224 Sms.CONTENT_URI, messageId); 225 226 SqliteWrapper.update(this, resolver, messageUri, values, 227 null, null); 228 return messageUri; 229 } 230 } finally { 231 cursor.close(); 232 } 233 return storeMessage(sms); 234 } 235 storeMessage(SmsMessage sms)236 private Uri storeMessage(SmsMessage sms) { 237 // Store the message in the content provider. 238 ContentValues values = extractContentValues(sms); 239 values.put(Inbox.BODY, sms.getDisplayMessageBody()); 240 ContentResolver resolver = getContentResolver(); 241 if (false) { 242 Log.d(TAG, "storeMessage " + this.toString()); 243 } 244 return SqliteWrapper.insert(this, resolver, Inbox.CONTENT_URI, values); 245 } 246 } 247