• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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