Initial checkin of AOSP Messaging app.
b/23110861 Change-Id: I11db999bd10656801e618f78ab2b2ef74136fff1
This commit is contained in:
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.messaging.ui.conversation;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.Formatter;
|
||||
|
||||
import com.android.messaging.Factory;
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.datamodel.BugleDatabaseOperations;
|
||||
import com.android.messaging.datamodel.DataModel;
|
||||
import com.android.messaging.datamodel.data.ConversationMessageData;
|
||||
import com.android.messaging.datamodel.data.ConversationParticipantsData;
|
||||
import com.android.messaging.datamodel.data.ParticipantData;
|
||||
import com.android.messaging.mmslib.pdu.PduHeaders;
|
||||
import com.android.messaging.sms.DatabaseMessages.MmsMessage;
|
||||
import com.android.messaging.sms.MmsUtils;
|
||||
import com.android.messaging.util.Assert;
|
||||
import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
|
||||
import com.android.messaging.util.Dates;
|
||||
import com.android.messaging.util.DebugUtils;
|
||||
import com.android.messaging.util.OsUtil;
|
||||
import com.android.messaging.util.PhoneUtils;
|
||||
import com.android.messaging.util.SafeAsyncTask;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MessageDetailsDialog {
|
||||
private static final String RECIPIENT_SEPARATOR = ", ";
|
||||
|
||||
// All methods are static, no creating this class
|
||||
private MessageDetailsDialog() {
|
||||
}
|
||||
|
||||
public static void show(final Context context, final ConversationMessageData data,
|
||||
final ConversationParticipantsData participants, final ParticipantData self) {
|
||||
if (DebugUtils.isDebugEnabled()) {
|
||||
new SafeAsyncTask<Void, Void, String>() {
|
||||
@Override
|
||||
protected String doInBackgroundTimed(Void... params) {
|
||||
return getMessageDetails(context, data, participants, self);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String messageDetails) {
|
||||
showDialog(context, messageDetails);
|
||||
}
|
||||
}.executeOnThreadPool(null, null, null);
|
||||
} else {
|
||||
String messageDetails = getMessageDetails(context, data, participants, self);
|
||||
showDialog(context, messageDetails);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getMessageDetails(final Context context,
|
||||
final ConversationMessageData data,
|
||||
final ConversationParticipantsData participants, final ParticipantData self) {
|
||||
String messageDetails = null;
|
||||
if (data.getIsSms()) {
|
||||
messageDetails = getSmsMessageDetails(data, participants, self);
|
||||
} else {
|
||||
// TODO: Handle SMS_TYPE_MMS_PUSH_NOTIFICATION type differently?
|
||||
messageDetails = getMmsMessageDetails(context, data, participants, self);
|
||||
}
|
||||
|
||||
return messageDetails;
|
||||
}
|
||||
|
||||
private static void showDialog(final Context context, String messageDetails) {
|
||||
if (!TextUtils.isEmpty(messageDetails)) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.message_details_title)
|
||||
.setMessage(messageDetails)
|
||||
.setCancelable(true)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string, separated by newlines, that contains a number of labels and values
|
||||
* for this sms message. The string will be displayed in a modal dialog.
|
||||
* @return string list of various message properties
|
||||
*/
|
||||
private static String getSmsMessageDetails(final ConversationMessageData data,
|
||||
final ConversationParticipantsData participants, final ParticipantData self) {
|
||||
final Resources res = Factory.get().getApplicationContext().getResources();
|
||||
final StringBuilder details = new StringBuilder();
|
||||
|
||||
// Type: Text message
|
||||
details.append(res.getString(R.string.message_type_label));
|
||||
details.append(res.getString(R.string.text_message));
|
||||
|
||||
// From: +1425xxxxxxx
|
||||
// or To: +1425xxxxxxx
|
||||
final String rawSender = data.getSenderNormalizedDestination();
|
||||
if (!TextUtils.isEmpty(rawSender)) {
|
||||
details.append('\n');
|
||||
details.append(res.getString(R.string.from_label));
|
||||
details.append(rawSender);
|
||||
}
|
||||
final String rawRecipients = getRecipientParticipantString(participants,
|
||||
data.getParticipantId(), data.getIsIncoming(), data.getSelfParticipantId());
|
||||
if (!TextUtils.isEmpty(rawRecipients)) {
|
||||
details.append('\n');
|
||||
details.append(res.getString(R.string.to_address_label));
|
||||
details.append(rawRecipients);
|
||||
}
|
||||
|
||||
// Sent: Mon 11:42AM
|
||||
if (data.getIsIncoming()) {
|
||||
if (data.getSentTimeStamp() != MmsUtils.INVALID_TIMESTAMP) {
|
||||
details.append('\n');
|
||||
details.append(res.getString(R.string.sent_label));
|
||||
details.append(
|
||||
Dates.getMessageDetailsTimeString(data.getSentTimeStamp()).toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Sent: Mon 11:43AM
|
||||
// or Received: Mon 11:43AM
|
||||
appendSentOrReceivedTimestamp(res, details, data);
|
||||
|
||||
appendSimInfo(res, self, details);
|
||||
|
||||
if (DebugUtils.isDebugEnabled()) {
|
||||
appendDebugInfo(details, data);
|
||||
}
|
||||
|
||||
return details.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string, separated by newlines, that contains a number of labels and values
|
||||
* for this mms message. The string will be displayed in a modal dialog.
|
||||
* @return string list of various message properties
|
||||
*/
|
||||
private static String getMmsMessageDetails(Context context, final ConversationMessageData data,
|
||||
final ConversationParticipantsData participants, final ParticipantData self) {
|
||||
final Resources res = Factory.get().getApplicationContext().getResources();
|
||||
// TODO: when we support non-auto-download of mms messages, we'll have to handle
|
||||
// the case when the message is a PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND and display
|
||||
// something different. See the Messaging app's MessageUtils.getNotificationIndDetails()
|
||||
|
||||
final StringBuilder details = new StringBuilder();
|
||||
|
||||
// Type: Multimedia message.
|
||||
details.append(res.getString(R.string.message_type_label));
|
||||
details.append(res.getString(R.string.multimedia_message));
|
||||
|
||||
// From: +1425xxxxxxx
|
||||
final String rawSender = data.getSenderNormalizedDestination();
|
||||
details.append('\n');
|
||||
details.append(res.getString(R.string.from_label));
|
||||
details.append(!TextUtils.isEmpty(rawSender) ? rawSender :
|
||||
res.getString(R.string.hidden_sender_address));
|
||||
|
||||
// To: +1425xxxxxxx
|
||||
final String rawRecipients = getRecipientParticipantString(participants,
|
||||
data.getParticipantId(), data.getIsIncoming(), data.getSelfParticipantId());
|
||||
if (!TextUtils.isEmpty(rawRecipients)) {
|
||||
details.append('\n');
|
||||
details.append(res.getString(R.string.to_address_label));
|
||||
details.append(rawRecipients);
|
||||
}
|
||||
|
||||
// Sent: Tue 3:05PM
|
||||
// or Received: Tue 3:05PM
|
||||
appendSentOrReceivedTimestamp(res, details, data);
|
||||
|
||||
// Subject: You're awesome
|
||||
details.append('\n');
|
||||
details.append(res.getString(R.string.subject_label));
|
||||
if (!TextUtils.isEmpty(MmsUtils.cleanseMmsSubject(res, data.getMmsSubject()))) {
|
||||
details.append(data.getMmsSubject());
|
||||
}
|
||||
|
||||
// Priority: High/Normal/Low
|
||||
details.append('\n');
|
||||
details.append(res.getString(R.string.priority_label));
|
||||
details.append(getPriorityDescription(res, data.getSmsPriority()));
|
||||
|
||||
// Message size: 30 KB
|
||||
if (data.getSmsMessageSize() > 0) {
|
||||
details.append('\n');
|
||||
details.append(res.getString(R.string.message_size_label));
|
||||
details.append(Formatter.formatFileSize(context, data.getSmsMessageSize()));
|
||||
}
|
||||
|
||||
appendSimInfo(res, self, details);
|
||||
|
||||
if (DebugUtils.isDebugEnabled()) {
|
||||
appendDebugInfo(details, data);
|
||||
}
|
||||
|
||||
return details.toString();
|
||||
}
|
||||
|
||||
private static void appendSentOrReceivedTimestamp(Resources res, StringBuilder details,
|
||||
ConversationMessageData data) {
|
||||
int labelId = -1;
|
||||
if (data.getIsIncoming()) {
|
||||
labelId = R.string.received_label;
|
||||
} else if (data.getIsSendComplete()) {
|
||||
labelId = R.string.sent_label;
|
||||
}
|
||||
if (labelId >= 0) {
|
||||
details.append('\n');
|
||||
details.append(res.getString(labelId));
|
||||
details.append(
|
||||
Dates.getMessageDetailsTimeString(data.getReceivedTimeStamp()).toString());
|
||||
}
|
||||
}
|
||||
|
||||
@DoesNotRunOnMainThread
|
||||
private static void appendDebugInfo(StringBuilder details, ConversationMessageData data) {
|
||||
// We grab the thread id from the database, so this needs to run in the background
|
||||
Assert.isNotMainThread();
|
||||
details.append("\n\n");
|
||||
details.append("DEBUG");
|
||||
|
||||
details.append('\n');
|
||||
details.append("Message id: ");
|
||||
details.append(data.getMessageId());
|
||||
|
||||
final String telephonyUri = data.getSmsMessageUri();
|
||||
details.append('\n');
|
||||
details.append("Telephony uri: ");
|
||||
details.append(telephonyUri);
|
||||
|
||||
final String conversationId = data.getConversationId();
|
||||
|
||||
if (conversationId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
details.append('\n');
|
||||
details.append("Conversation id: ");
|
||||
details.append(conversationId);
|
||||
|
||||
final long threadId = BugleDatabaseOperations.getThreadId(DataModel.get().getDatabase(),
|
||||
conversationId);
|
||||
|
||||
details.append('\n');
|
||||
details.append("Conversation telephony thread id: ");
|
||||
details.append(threadId);
|
||||
|
||||
MmsMessage mms = null;
|
||||
|
||||
if (data.getIsMms()) {
|
||||
if (telephonyUri == null) {
|
||||
return;
|
||||
}
|
||||
mms = MmsUtils.loadMms(Uri.parse(telephonyUri));
|
||||
if (mms == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We log the thread id again to check that they are internally consistent
|
||||
final long mmsThreadId = mms.mThreadId;
|
||||
details.append('\n');
|
||||
details.append("Telephony thread id: ");
|
||||
details.append(mmsThreadId);
|
||||
|
||||
// Log the MMS content location
|
||||
final String mmsContentLocation = mms.mContentLocation;
|
||||
details.append('\n');
|
||||
details.append("Content location URL: ");
|
||||
details.append(mmsContentLocation);
|
||||
}
|
||||
|
||||
final String recipientsString = MmsUtils.getRawRecipientIdsForThread(threadId);
|
||||
if (recipientsString != null) {
|
||||
details.append('\n');
|
||||
details.append("Thread recipient ids: ");
|
||||
details.append(recipientsString);
|
||||
}
|
||||
|
||||
final List<String> recipients = MmsUtils.getRecipientsByThread(threadId);
|
||||
if (recipients != null) {
|
||||
details.append('\n');
|
||||
details.append("Thread recipients: ");
|
||||
details.append(recipients.toString());
|
||||
|
||||
if (mms != null) {
|
||||
final String from = MmsUtils.getMmsSender(recipients, mms.getUri());
|
||||
details.append('\n');
|
||||
details.append("Sender: ");
|
||||
details.append(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getRecipientParticipantString(
|
||||
final ConversationParticipantsData participants, final String senderId,
|
||||
final boolean addSelf, final String selfId) {
|
||||
final StringBuilder recipients = new StringBuilder();
|
||||
for (final ParticipantData participant : participants) {
|
||||
if (TextUtils.equals(participant.getId(), senderId)) {
|
||||
// Don't add sender
|
||||
continue;
|
||||
}
|
||||
if (participant.isSelf() &&
|
||||
(!participant.getId().equals(selfId) || !addSelf)) {
|
||||
// For self participants, don't add the one that's not relevant to this message
|
||||
// or if we are asked not to add self
|
||||
continue;
|
||||
}
|
||||
final String phoneNumber = participant.getNormalizedDestination();
|
||||
// Don't add empty number. This should not happen. But if that happens
|
||||
// we should not add it.
|
||||
if (!TextUtils.isEmpty(phoneNumber)) {
|
||||
if (recipients.length() > 0) {
|
||||
recipients.append(RECIPIENT_SEPARATOR);
|
||||
}
|
||||
recipients.append(phoneNumber);
|
||||
}
|
||||
}
|
||||
return recipients.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the numeric mms priority into a human-readable string
|
||||
* @param res
|
||||
* @param priorityValue coded PduHeader priority
|
||||
* @return string representation of the priority
|
||||
*/
|
||||
private static String getPriorityDescription(final Resources res, final int priorityValue) {
|
||||
switch(priorityValue) {
|
||||
case PduHeaders.PRIORITY_HIGH:
|
||||
return res.getString(R.string.priority_high);
|
||||
case PduHeaders.PRIORITY_LOW:
|
||||
return res.getString(R.string.priority_low);
|
||||
case PduHeaders.PRIORITY_NORMAL:
|
||||
default:
|
||||
return res.getString(R.string.priority_normal);
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendSimInfo(final Resources res,
|
||||
final ParticipantData self, final StringBuilder outString) {
|
||||
if (!OsUtil.isAtLeastL_MR1()
|
||||
|| self == null
|
||||
|| PhoneUtils.getDefault().getActiveSubscriptionCount() < 2) {
|
||||
return;
|
||||
}
|
||||
// The appended SIM info would look like:
|
||||
// SIM: SUB 01
|
||||
// or SIM: SIM 1
|
||||
// or SIM: Unknown
|
||||
Assert.isTrue(self.isSelf());
|
||||
outString.append('\n');
|
||||
outString.append(res.getString(R.string.sim_label));
|
||||
if (self.isActiveSubscription() && !self.isDefaultSelf()) {
|
||||
final String subscriptionName = self.getSubscriptionName();
|
||||
if (TextUtils.isEmpty(subscriptionName)) {
|
||||
outString.append(res.getString(R.string.sim_slot_identifier,
|
||||
self.getDisplaySlotId()));
|
||||
} else {
|
||||
outString.append(subscriptionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user