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