1 /* 2 * Copyright (C) 2008 Esmertec AG. 3 * Copyright (C) 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 java.util.ArrayList; 21 import java.util.HashMap; 22 import java.util.Iterator; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.Set; 26 27 import android.app.ListActivity; 28 import android.content.Intent; 29 import android.database.Cursor; 30 import android.database.sqlite.SqliteWrapper; 31 import android.net.Uri; 32 import android.os.Bundle; 33 import android.provider.Telephony.Mms; 34 import android.provider.Telephony.Sms; 35 import android.telephony.PhoneNumberUtils; 36 import android.text.TextUtils; 37 import android.util.Log; 38 import android.view.LayoutInflater; 39 import android.view.View; 40 import android.view.Window; 41 import android.widget.ListView; 42 43 import com.android.mms.R; 44 import com.google.android.mms.pdu.PduHeaders; 45 46 /** 47 * This is the UI for displaying a delivery report: 48 * 49 * This activity can handle the following parameters from the intent 50 * by which it is launched: 51 * 52 * thread_id long The id of the conversation from which to get the recipients 53 * for the report. 54 * message_id long The id of the message about which a report should be displayed. 55 * message_type String The type of message (Sms or Mms). This is used in 56 * conjunction with the message id to retrive the particular message that 57 * the report will be about. 58 */ 59 public class DeliveryReportActivity extends ListActivity { 60 private static final String LOG_TAG = "DeliveryReportActivity"; 61 62 static final String[] MMS_REPORT_REQUEST_PROJECTION = new String[] { 63 Mms.Addr.ADDRESS, //0 64 Mms.DELIVERY_REPORT, //1 65 Mms.READ_REPORT //2 66 }; 67 68 static final String[] MMS_REPORT_STATUS_PROJECTION = new String[] { 69 Mms.Addr.ADDRESS, //0 70 "delivery_status", //1 71 "read_status" //2 72 }; 73 74 static final String[] SMS_REPORT_STATUS_PROJECTION = new String[] { 75 Sms.ADDRESS, //0 76 Sms.STATUS, //1 77 Sms.DATE_SENT, //2 78 Sms.TYPE //3 79 }; 80 81 // These indices must sync up with the projections above. 82 static final int COLUMN_RECIPIENT = 0; 83 static final int COLUMN_DELIVERY_REPORT = 1; 84 static final int COLUMN_READ_REPORT = 2; 85 static final int COLUMN_DELIVERY_STATUS = 1; 86 static final int COLUMN_READ_STATUS = 2; 87 static final int COLUMN_DATE_SENT = 2; 88 static final int COLUMN_MESSAGE_TYPE = 3; 89 90 private long mMessageId; 91 private String mMessageType; 92 93 @Override 94 protected void onCreate(Bundle icicle) { 95 super.onCreate(icicle); 96 requestWindowFeature(Window.FEATURE_NO_TITLE); 97 setContentView(R.layout.delivery_report_activity); 98 99 Intent intent = getIntent(); 100 mMessageId = getMessageId(icicle, intent); 101 mMessageType = getMessageType(icicle, intent); 102 103 initListView(); 104 initListAdapter(); 105 } 106 107 private void initListView() { 108 // Add the header for the list view. 109 LayoutInflater inflater = getLayoutInflater(); 110 View header = inflater.inflate(R.layout.delivery_report_header, null); 111 getListView().addHeaderView(header, null, true); 112 } 113 114 private void initListAdapter() { 115 List<DeliveryReportItem> items = getReportItems(); 116 if (items == null) { 117 items = new ArrayList<DeliveryReportItem>(1); 118 items.add(new DeliveryReportItem("", getString(R.string.status_none), null)); 119 Log.w(LOG_TAG, "cursor == null"); 120 } 121 setListAdapter(new DeliveryReportAdapter(this, items)); 122 } 123 124 @Override 125 public void onResume() { 126 super.onResume(); 127 refreshDeliveryReport(); 128 } 129 130 private void refreshDeliveryReport() { 131 ListView list = getListView(); 132 list.invalidateViews(); 133 list.requestFocus(); 134 } 135 136 private long getMessageId(Bundle icicle, Intent intent) { 137 long msgId = 0L; 138 139 if (icicle != null) { 140 msgId = icicle.getLong("message_id"); 141 } 142 143 if (msgId == 0L) { 144 msgId = intent.getLongExtra("message_id", 0L); 145 } 146 147 return msgId; 148 } 149 150 private String getMessageType(Bundle icicle, Intent intent) { 151 String msgType = null; 152 153 if (icicle != null) { 154 msgType = icicle.getString("message_type"); 155 } 156 157 if (msgType == null) { 158 msgType = intent.getStringExtra("message_type"); 159 } 160 161 return msgType; 162 } 163 164 private List<DeliveryReportItem> getReportItems() { 165 if (mMessageType.equals("sms")) { 166 return getSmsReportItems(); 167 } else { 168 return getMmsReportItems(); 169 } 170 } 171 172 private List<DeliveryReportItem> getSmsReportItems() { 173 String selection = "_id = " + mMessageId; 174 Cursor c = SqliteWrapper.query(this, getContentResolver(), Sms.CONTENT_URI, 175 SMS_REPORT_STATUS_PROJECTION, selection, null, null); 176 if (c == null) { 177 return null; 178 } 179 180 try { 181 if (c.getCount() <= 0) { 182 return null; 183 } 184 185 List<DeliveryReportItem> items = new ArrayList<DeliveryReportItem>(); 186 while (c.moveToNext()) { 187 // For sent messages with delivery reports, we stick the delivery time in the 188 // date_sent column (see MessageStatusReceiver). 189 String deliveryDateString = null; 190 long deliveryDate = c.getLong(COLUMN_DATE_SENT); 191 int messageType = c.getInt(COLUMN_MESSAGE_TYPE); 192 if (messageType == Sms.MESSAGE_TYPE_SENT && deliveryDate > 0) { 193 deliveryDateString = getString(R.string.delivered_label) + 194 MessageUtils.formatTimeStampString(this, 195 deliveryDate, true); 196 } 197 198 items.add(new DeliveryReportItem( 199 getString(R.string.recipient_label) + c.getString(COLUMN_RECIPIENT), 200 getString(R.string.status_label) + 201 getSmsStatusText(c.getInt(COLUMN_DELIVERY_STATUS)), 202 deliveryDateString)); 203 } 204 return items; 205 } finally { 206 c.close(); 207 } 208 } 209 210 private String getMmsReportStatusText( 211 MmsReportRequest request, 212 Map<String, MmsReportStatus> reportStatus) { 213 if (reportStatus == null) { 214 // haven't received any reports. 215 return getString(R.string.status_pending); 216 } 217 218 String recipient = request.getRecipient(); 219 recipient = (Mms.isEmailAddress(recipient))? 220 Mms.extractAddrSpec(recipient): PhoneNumberUtils.stripSeparators(recipient); 221 MmsReportStatus status = queryStatusByRecipient(reportStatus, recipient); 222 if (status == null) { 223 // haven't received any reports. 224 return getString(R.string.status_pending); 225 } 226 227 if (request.isReadReportRequested()) { 228 if (status.readStatus != 0) { 229 switch (status.readStatus) { 230 case PduHeaders.READ_STATUS_READ: 231 return getString(R.string.status_read); 232 case PduHeaders.READ_STATUS__DELETED_WITHOUT_BEING_READ: 233 return getString(R.string.status_unread); 234 } 235 } 236 } 237 238 switch (status.deliveryStatus) { 239 case 0: // No delivery report received so far. 240 return getString(R.string.status_pending); 241 case PduHeaders.STATUS_FORWARDED: 242 case PduHeaders.STATUS_RETRIEVED: 243 return getString(R.string.status_received); 244 case PduHeaders.STATUS_REJECTED: 245 return getString(R.string.status_rejected); 246 default: 247 return getString(R.string.status_failed); 248 } 249 } 250 251 private static MmsReportStatus queryStatusByRecipient( 252 Map<String, MmsReportStatus> status, String recipient) { 253 Set<String> recipientSet = status.keySet(); 254 Iterator<String> iterator = recipientSet.iterator(); 255 while (iterator.hasNext()) { 256 String r = iterator.next(); 257 if (Mms.isEmailAddress(recipient)) { 258 if (TextUtils.equals(r, recipient)) { 259 return status.get(r); 260 } 261 } 262 else if (PhoneNumberUtils.compare(r, recipient)) { 263 return status.get(r); 264 } 265 } 266 return null; 267 } 268 269 private List<DeliveryReportItem> getMmsReportItems() { 270 List<MmsReportRequest> reportReqs = getMmsReportRequests(); 271 if (null == reportReqs) { 272 return null; 273 } 274 275 if (reportReqs.size() == 0) { 276 return null; 277 } 278 279 Map<String, MmsReportStatus> reportStatus = getMmsReportStatus(); 280 List<DeliveryReportItem> items = new ArrayList<DeliveryReportItem>(); 281 for (MmsReportRequest reportReq : reportReqs) { 282 String statusText = getString(R.string.status_label) + 283 getMmsReportStatusText(reportReq, reportStatus); 284 items.add(new DeliveryReportItem(getString(R.string.recipient_label) + 285 reportReq.getRecipient(), statusText, null)); 286 } 287 return items; 288 } 289 290 private Map<String, MmsReportStatus> getMmsReportStatus() { 291 Uri uri = Uri.withAppendedPath(Mms.REPORT_STATUS_URI, 292 String.valueOf(mMessageId)); 293 Cursor c = SqliteWrapper.query(this, getContentResolver(), uri, 294 MMS_REPORT_STATUS_PROJECTION, null, null, null); 295 296 if (c == null) { 297 return null; 298 } 299 300 try { 301 Map<String, MmsReportStatus> statusMap = 302 new HashMap<String, MmsReportStatus>(); 303 304 while (c.moveToNext()) { 305 String recipient = c.getString(COLUMN_RECIPIENT); 306 recipient = (Mms.isEmailAddress(recipient))? 307 Mms.extractAddrSpec(recipient): 308 PhoneNumberUtils.stripSeparators(recipient); 309 MmsReportStatus status = new MmsReportStatus( 310 c.getInt(COLUMN_DELIVERY_STATUS), 311 c.getInt(COLUMN_READ_STATUS)); 312 statusMap.put(recipient, status); 313 } 314 return statusMap; 315 } finally { 316 c.close(); 317 } 318 } 319 320 private List<MmsReportRequest> getMmsReportRequests() { 321 Uri uri = Uri.withAppendedPath(Mms.REPORT_REQUEST_URI, 322 String.valueOf(mMessageId)); 323 Cursor c = SqliteWrapper.query(this, getContentResolver(), uri, 324 MMS_REPORT_REQUEST_PROJECTION, null, null, null); 325 326 if (c == null) { 327 return null; 328 } 329 330 try { 331 if (c.getCount() <= 0) { 332 return null; 333 } 334 335 List<MmsReportRequest> reqList = new ArrayList<MmsReportRequest>(); 336 while (c.moveToNext()) { 337 reqList.add(new MmsReportRequest( 338 c.getString(COLUMN_RECIPIENT), 339 c.getInt(COLUMN_DELIVERY_REPORT), 340 c.getInt(COLUMN_READ_REPORT))); 341 } 342 return reqList; 343 } finally { 344 c.close(); 345 } 346 } 347 348 private String getSmsStatusText(int status) { 349 if (status == Sms.STATUS_NONE) { 350 // No delivery report requested 351 return getString(R.string.status_none); 352 } else if (status >= Sms.STATUS_FAILED) { 353 // Failure 354 return getString(R.string.status_failed); 355 } else if (status >= Sms.STATUS_PENDING) { 356 // Pending 357 return getString(R.string.status_pending); 358 } else { 359 // Success 360 return getString(R.string.status_received); 361 } 362 } 363 364 private static final class MmsReportStatus { 365 final int deliveryStatus; 366 final int readStatus; 367 368 public MmsReportStatus(int drStatus, int rrStatus) { 369 deliveryStatus = drStatus; 370 readStatus = rrStatus; 371 } 372 } 373 374 private static final class MmsReportRequest { 375 private final String mRecipient; 376 private final boolean mIsDeliveryReportRequsted; 377 private final boolean mIsReadReportRequested; 378 379 public MmsReportRequest(String recipient, int drValue, int rrValue) { 380 mRecipient = recipient; 381 mIsDeliveryReportRequsted = drValue == PduHeaders.VALUE_YES; 382 mIsReadReportRequested = rrValue == PduHeaders.VALUE_YES; 383 } 384 385 public String getRecipient() { 386 return mRecipient; 387 } 388 389 public boolean isDeliveryReportRequested() { 390 return mIsDeliveryReportRequsted; 391 } 392 393 public boolean isReadReportRequested() { 394 return mIsReadReportRequested; 395 } 396 } 397 } 398