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