Home | History | Annotate | Download | only in ui
      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.content.Intent;
     28 import android.database.Cursor;
     29 import android.database.sqlite.SqliteWrapper;
     30 import android.net.Uri;
     31 import android.os.Bundle;
     32 import android.os.Handler;
     33 import android.os.Message;
     34 import android.os.SystemClock;
     35 import android.provider.Telephony.Sms;
     36 import android.provider.Telephony.Sms.Inbox;
     37 import android.telephony.SmsMessage;
     38 import android.text.TextUtils;
     39 import android.util.Log;
     40 import android.view.Window;
     41 
     42 import com.android.mms.LogTag;
     43 import com.android.mms.R;
     44 import com.android.mms.transaction.MessagingNotification;
     45 
     46 import java.util.ArrayList;
     47 
     48 /**
     49  * Display a class-zero SMS message to the user. Wait for the user to dismiss
     50  * it.
     51  */
     52 public class ClassZeroActivity extends Activity {
     53     private static final String TAG = LogTag.TAG;
     54     private static final int ON_AUTO_SAVE = 1;
     55     private static final String[] REPLACE_PROJECTION = new String[] { Sms._ID,
     56             Sms.ADDRESS, Sms.PROTOCOL };
     57     private static final int REPLACE_COLUMN_ID = 0;
     58 
     59     /** Default timer to dismiss the dialog. */
     60     private static final long DEFAULT_TIMER = 5 * 60 * 1000;
     61 
     62     /** To remember the exact time when the timer should fire. */
     63     private static final String TIMER_FIRE = "timer_fire";
     64 
     65     private SmsMessage mMessage = null;
     66 
     67     /** Is the message read. */
     68     private boolean mRead = false;
     69 
     70     /** The timer to dismiss the dialog automatically. */
     71     private long mTimerSet = 0;
     72     private AlertDialog mDialog = null;
     73 
     74     private ArrayList<SmsMessage> mMessageQueue = null;
     75 
     76     private Handler mHandler = new Handler() {
     77         @Override
     78         public void handleMessage(Message msg) {
     79             // Do not handle an invalid message.
     80             if (msg.what == ON_AUTO_SAVE) {
     81                 mRead = false;
     82                 mDialog.dismiss();
     83                 saveMessage();
     84                 processNextMessage();
     85             }
     86         }
     87     };
     88 
     89     private boolean queueMsgFromIntent(Intent msgIntent) {
     90         byte[] pdu = msgIntent.getByteArrayExtra("pdu");
     91         String format = msgIntent.getStringExtra("format");
     92         SmsMessage rawMessage = SmsMessage.createFromPdu(pdu, format);
     93         String message = rawMessage.getMessageBody();
     94         if (TextUtils.isEmpty(message)) {
     95             if (mMessageQueue.size() == 0) {
     96                 finish();
     97             }
     98             return false;
     99         }
    100         mMessageQueue.add(rawMessage);
    101         return true;
    102     }
    103 
    104     private void processNextMessage() {
    105         mMessageQueue.remove(0);
    106         if (mMessageQueue.size() == 0) {
    107             finish();
    108         } else {
    109             displayZeroMessage(mMessageQueue.get(0));
    110         }
    111     }
    112 
    113     private void saveMessage() {
    114         Uri messageUri = null;
    115         if (mMessage.isReplace()) {
    116             messageUri = replaceMessage(mMessage);
    117         } else {
    118             messageUri = storeMessage(mMessage);
    119         }
    120         if (!mRead && messageUri != null) {
    121             MessagingNotification.nonBlockingUpdateNewMessageIndicator(
    122                     this,
    123                     MessagingNotification.THREAD_ALL,   // always notify on class-zero msgs
    124                     false);
    125         }
    126     }
    127 
    128     @Override
    129     protected void onNewIntent(Intent msgIntent) {
    130         /* Running with another visible message, queue this one */
    131         queueMsgFromIntent(msgIntent);
    132     }
    133 
    134     @Override
    135     protected void onCreate(Bundle icicle) {
    136         super.onCreate(icicle);
    137         requestWindowFeature(Window.FEATURE_NO_TITLE);
    138         getWindow().setBackgroundDrawableResource(
    139                 R.drawable.class_zero_background);
    140 
    141         if (mMessageQueue == null) {
    142             mMessageQueue = new ArrayList<SmsMessage>();
    143         }
    144 
    145         if (!queueMsgFromIntent(getIntent())) {
    146             return;
    147         }
    148 
    149         if (mMessageQueue.size() == 1) {
    150             displayZeroMessage(mMessageQueue.get(0));
    151         }
    152 
    153         if (icicle != null) {
    154             mTimerSet = icicle.getLong(TIMER_FIRE, mTimerSet);
    155         }
    156     }
    157 
    158     private void displayZeroMessage(SmsMessage rawMessage) {
    159         String message = rawMessage.getMessageBody();
    160         /* This'll be used by the save action */
    161         mMessage = rawMessage;
    162 
    163         mDialog = new AlertDialog.Builder(this, AlertDialog.THEME_HOLO_DARK).setMessage(message)
    164                 .setPositiveButton(R.string.save, mSaveListener)
    165                 .setNegativeButton(android.R.string.cancel, mCancelListener)
    166                 .setCancelable(false).show();
    167         long now = SystemClock.uptimeMillis();
    168         mTimerSet = now + DEFAULT_TIMER;
    169     }
    170 
    171     @Override
    172     protected void onStart() {
    173         super.onStart();
    174         long now = SystemClock.uptimeMillis();
    175         if (mTimerSet <= now) {
    176             // Save the message if the timer already expired.
    177             mHandler.sendEmptyMessage(ON_AUTO_SAVE);
    178         } else {
    179             mHandler.sendEmptyMessageAtTime(ON_AUTO_SAVE, mTimerSet);
    180             if (false) {
    181                 Log.d(TAG, "onRestart time = " + Long.toString(mTimerSet) + " "
    182                         + this.toString());
    183             }
    184         }
    185     }
    186 
    187     @Override
    188     protected void onSaveInstanceState(Bundle outState) {
    189         super.onSaveInstanceState(outState);
    190         outState.putLong(TIMER_FIRE, mTimerSet);
    191         if (false) {
    192             Log.d(TAG, "onSaveInstanceState time = " + Long.toString(mTimerSet)
    193                     + " " + this.toString());
    194         }
    195     }
    196 
    197     @Override
    198     protected void onStop() {
    199         super.onStop();
    200         mHandler.removeMessages(ON_AUTO_SAVE);
    201         if (false) {
    202             Log.d(TAG, "onStop time = " + Long.toString(mTimerSet)
    203                     + " " + this.toString());
    204         }
    205     }
    206 
    207     private final OnClickListener mCancelListener = new OnClickListener() {
    208         public void onClick(DialogInterface dialog, int whichButton) {
    209             dialog.dismiss();
    210             processNextMessage();
    211         }
    212     };
    213 
    214     private final OnClickListener mSaveListener = new OnClickListener() {
    215         public void onClick(DialogInterface dialog, int whichButton) {
    216             mRead = true;
    217             saveMessage();
    218             dialog.dismiss();
    219             processNextMessage();
    220         }
    221     };
    222 
    223     private ContentValues extractContentValues(SmsMessage sms) {
    224         // Store the message in the content provider.
    225         ContentValues values = new ContentValues();
    226 
    227         values.put(Inbox.ADDRESS, sms.getDisplayOriginatingAddress());
    228 
    229         // Use now for the timestamp to avoid confusion with clock
    230         // drift between the handset and the SMSC.
    231         values.put(Inbox.DATE, new Long(System.currentTimeMillis()));
    232         values.put(Inbox.PROTOCOL, sms.getProtocolIdentifier());
    233         values.put(Inbox.READ, Integer.valueOf(mRead ? 1 : 0));
    234         values.put(Inbox.SEEN, Integer.valueOf(mRead ? 1 : 0));
    235 
    236         if (sms.getPseudoSubject().length() > 0) {
    237             values.put(Inbox.SUBJECT, sms.getPseudoSubject());
    238         }
    239         values.put(Inbox.REPLY_PATH_PRESENT, sms.isReplyPathPresent() ? 1 : 0);
    240         values.put(Inbox.SERVICE_CENTER, sms.getServiceCenterAddress());
    241         return values;
    242     }
    243 
    244     private Uri replaceMessage(SmsMessage sms) {
    245         ContentValues values = extractContentValues(sms);
    246 
    247         values.put(Inbox.BODY, sms.getMessageBody());
    248 
    249         ContentResolver resolver = getContentResolver();
    250         String originatingAddress = sms.getOriginatingAddress();
    251         int protocolIdentifier = sms.getProtocolIdentifier();
    252         String selection = Sms.ADDRESS + " = ? AND " + Sms.PROTOCOL + " = ?";
    253         String[] selectionArgs = new String[] { originatingAddress,
    254                 Integer.toString(protocolIdentifier) };
    255 
    256         Cursor cursor = SqliteWrapper.query(this, resolver, Inbox.CONTENT_URI,
    257                 REPLACE_PROJECTION, selection, selectionArgs, null);
    258 
    259         try {
    260             if (cursor.moveToFirst()) {
    261                 long messageId = cursor.getLong(REPLACE_COLUMN_ID);
    262                 Uri messageUri = ContentUris.withAppendedId(
    263                         Sms.CONTENT_URI, messageId);
    264 
    265                 SqliteWrapper.update(this, resolver, messageUri, values,
    266                         null, null);
    267                 return messageUri;
    268             }
    269         } finally {
    270             cursor.close();
    271         }
    272         return storeMessage(sms);
    273     }
    274 
    275     private Uri storeMessage(SmsMessage sms) {
    276         // Store the message in the content provider.
    277         ContentValues values = extractContentValues(sms);
    278         values.put(Inbox.BODY, sms.getDisplayMessageBody());
    279         ContentResolver resolver = getContentResolver();
    280         if (false) {
    281             Log.d(TAG, "storeMessage " + this.toString());
    282         }
    283         return SqliteWrapper.insert(this, resolver, Inbox.CONTENT_URI, values);
    284     }
    285 }
    286