summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortnmy-cyngn <tlnu@cyngn.com>2016-05-09 01:22:13 +0530
committerGerrit Code Review <gerrit@cyanogenmod.org>2016-05-11 10:38:17 -0700
commit0dabdec1f6f6f90b6a0cd45646bdbf5fa79cde74 (patch)
tree7a7cdf30f367db4f93ad187f00ff980bf3b5bbd5
parent1f769b00e211fae9f482f063e27efeab39c874ab (diff)
downloadandroid_packages_apps_Messaging-0dabdec1f6f6f90b6a0cd45646bdbf5fa79cde74.tar.gz
android_packages_apps_Messaging-0dabdec1f6f6f90b6a0cd45646bdbf5fa79cde74.tar.bz2
android_packages_apps_Messaging-0dabdec1f6f6f90b6a0cd45646bdbf5fa79cde74.zip
Save messages to SIM feature
Porting save message to SIM feature from 12.1 to 13.0 Bug-Id: PAELLA-221 Change-Id: Id340d2cd5bb1ec0fa82cfbea8575999e65f4c34e
-rw-r--r--AndroidManifest.xml5
-rw-r--r--res/drawable-hdpi/ic_overflow_dark.pngbin0 -> 2834 bytes
-rw-r--r--res/drawable-mdpi/ic_overflow_dark.pngbin0 -> 2772 bytes
-rw-r--r--res/drawable-xhdpi/ic_overflow_dark.pngbin0 -> 2905 bytes
-rw-r--r--res/drawable-xxhdpi/ic_overflow_dark.pngbin0 -> 3057 bytes
-rw-r--r--res/layout/sim_list.xml41
-rw-r--r--res/menu/conversation_fragment_select_menu.xml10
-rw-r--r--res/menu/sim_msg_multi_select_menu.xml38
-rw-r--r--res/values/cm_strings.xml49
-rw-r--r--res/xml-v21/preferences_application.xml10
-rw-r--r--res/xml-v23/preferences_application.xml10
-rw-r--r--res/xml/preferences_application.xml9
-rw-r--r--src/com/android/messaging/datamodel/data/ConversationMessageData.java20
-rw-r--r--src/com/android/messaging/datamodel/data/SimMessageData.java34
-rw-r--r--src/com/android/messaging/sms/SimMessagesUtils.java469
-rw-r--r--src/com/android/messaging/ui/ManageSimMessages.java394
-rw-r--r--src/com/android/messaging/ui/appsettings/ApplicationSettingsActivity.java72
-rw-r--r--src/com/android/messaging/ui/conversation/ConversationFragment.java63
-rw-r--r--src/com/android/messaging/ui/conversation/ConversationMessageAdapter.java18
-rw-r--r--src/com/android/messaging/ui/conversation/ConversationMessageBubbleView.java6
-rw-r--r--src/com/android/messaging/ui/conversation/ConversationMessageView.java19
21 files changed, 1248 insertions, 19 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0b90c39..7563f9c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -545,6 +545,11 @@
<receiver android:name="com.cyanogenmod.messaging.lookup.LookupProviderManager" />
+ <activity android:name=".ui.ManageSimMessages"
+ android:theme="@style/BugleTheme"
+ android:launchMode="singleTop"
+ android:configChanges="orientation|screenSize|keyboardHidden"
+ android:label="@string/sim_manage_messages_title" />
</application>
</manifest>
diff --git a/res/drawable-hdpi/ic_overflow_dark.png b/res/drawable-hdpi/ic_overflow_dark.png
new file mode 100644
index 0000000..e9224e5
--- /dev/null
+++ b/res/drawable-hdpi/ic_overflow_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_overflow_dark.png b/res/drawable-mdpi/ic_overflow_dark.png
new file mode 100644
index 0000000..685b4ea
--- /dev/null
+++ b/res/drawable-mdpi/ic_overflow_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_overflow_dark.png b/res/drawable-xhdpi/ic_overflow_dark.png
new file mode 100644
index 0000000..74b258f
--- /dev/null
+++ b/res/drawable-xhdpi/ic_overflow_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_overflow_dark.png b/res/drawable-xxhdpi/ic_overflow_dark.png
new file mode 100644
index 0000000..fff1b18
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_overflow_dark.png
Binary files differ
diff --git a/res/layout/sim_list.xml b/res/layout/sim_list.xml
new file mode 100644
index 0000000..daf5209
--- /dev/null
+++ b/res/layout/sim_list.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 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.
+ */
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:layout_gravity="center">
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:listSelector="@android:color/transparent"
+ android:clipToPadding="false" />
+ <TextView
+ android:id="@+id/empty_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sim_empty"
+ android:layout_gravity="center"
+ android:visibility="gone" />
+</FrameLayout>
diff --git a/res/menu/conversation_fragment_select_menu.xml b/res/menu/conversation_fragment_select_menu.xml
index 7ce0829..f76deed 100644
--- a/res/menu/conversation_fragment_select_menu.xml
+++ b/res/menu/conversation_fragment_select_menu.xml
@@ -60,4 +60,14 @@
android:icon="@drawable/ic_delete_small_dark"
android:title="@string/action_delete_message"
appcompat:showAsAction="collapseActionView|always"/>
+ <item
+ android:icon="@drawable/ic_overflow_dark"
+ appcompat:showAsAction="collapseActionView|always">
+ <menu>
+ <item
+ android:id="@+id/copy_to_sim"
+ appcompat:showAsAction="never"
+ android:title="@string/operation_to_card_memory"/>
+ </menu>
+ </item>
</menu>
diff --git a/res/menu/sim_msg_multi_select_menu.xml b/res/menu/sim_msg_multi_select_menu.xml
new file mode 100644
index 0000000..057adc8
--- /dev/null
+++ b/res/menu/sim_msg_multi_select_menu.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2014, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:appcompat="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/delete"
+ android:icon="@drawable/ic_delete_small_dark"
+ appcompat:showAsAction="always"
+ android:title="@string/delete"/>
+</menu>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index 4ad47e2..b4db481 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -95,4 +95,53 @@
<string name="show_emoticons_pref_summary">Show the emoticons key on the keyboard</string>
<string name="select_phone_account_title">Send with</string>
+
+ <!-- Copy message to sim -->
+ <!-- Settings item for entering SIM card message screen -->
+ <string name="pref_title_manage_sim_messages">Manage SIM card messages</string>
+ <string name="pref_title_manage_sim_messages_slot1">Manage SIM 1 messages</string>
+ <string name="pref_title_manage_sim_messages_slot2">Manage SIM 2 messages</string>
+ <string name="copy_to_sim">Copy message to SIM card</string>
+ <string name="copy_to_sim_success"><xliff:g id="count">%1$d</xliff:g> of <xliff:g id="total">%2$d</xliff:g> messages saved to SIM card.</string>
+ <string name="copy_to_sim_fail">Unable to save message</string>
+ <string name="copy_to_phone_success">Message saved</string>
+ <string name="copy_to_phone_fail">Unable to save message</string>
+ <!-- Message that appears on the "SMS messges on SIM card" screen when there are no messages on the SIM card -->
+ <string name="sim_empty">No messages on SIM card.</string>
+ <!-- Context menu item on an individual message for emailing the sender -->
+ <string name="menu_send_email">Send email to <xliff:g id="name">%s</xliff:g></string>
+ <!-- Context menu item on an individual message for dialing the sender's phone number -->
+ <string name="menu_call_back">Call <xliff:g id="name">%s</xliff:g></string>
+ <!-- Menu shown in the context menu when long pressing on a message when the message contains
+ one or more phone numbers and/or email addresses and the number or email is new to
+ contacts. [CHAR LIMIT=50] -->
+ <string name="menu_add_address_to_contacts">Add <xliff:g id="contactEmailOrNumber">%s</xliff:g> to People</string>
+ <string name="sim_capacity_title">SIM capacity</string>
+ <string name="sim_capacity"><xliff:g id="used">%1$d</xliff:g> of <xliff:g id="total">%2$d</xliff:g> SIM card entries are used</string>
+ <!-- Confirm Delete -->
+ <!-- Delete confirmation dialog title -->
+ <string name="confirm_dialog_title">Delete?</string>
+ <!-- Title of screen displaying messages on SIM card -->
+ <string name="sim_manage_messages_title">Text messages on SIM card</string>
+ <!-- Message that appears on screen while SIM card messages are retrieved -->
+ <string name="refreshing">Refreshing\u2026</string>
+ <string name="confirm_delete_selected_messages">Selected message will be deleted.</string>
+ <!-- Delete button title -->
+ <string name="delete">Delete</string>
+ <!-- Context menu item on an individual message for forwarding it to another recipient -->
+ <string name="menu_forward">Forward</string>
+ <string name="menu_reply">Reply</string>
+ <!-- Menu title to copy a selected message from the SIM card to the phone. -->
+ <string name="sim_copy_to_phone_memory" product="tablet">Copy to tablet memory</string>
+ <!-- Menu title to copy a selected message from the SIM card to the phone. -->
+ <string name="sim_copy_to_phone_memory" product="default">Copy to phone memory</string>
+ <string name="selected_all">Select all</string>
+ <string name="deselected_all">Deselect all</string>
+ <string name="selected_count"><xliff:g id="COUNT">%d</xliff:g> selected</string>
+ <!-- Text to be used for affirmative response button in dialog boxes -->
+ <string name="yes">OK</string>
+ <!-- Copy message to sim -->
+ <string name="operation_to_card_memory">Copy message to SIM card</string>
+ <string name="slot1">SIM 1</string>
+ <string name="slot2">SIM 2</string>
</resources>
diff --git a/res/xml-v21/preferences_application.xml b/res/xml-v21/preferences_application.xml
index 4d39999..603ddb6 100644
--- a/res/xml-v21/preferences_application.xml
+++ b/res/xml-v21/preferences_application.xml
@@ -145,6 +145,16 @@
android:title="@string/mms_delete_pref_title" />
</PreferenceCategory>
+ <Preference
+ android:key="pref_key_manage_sim_messages"
+ android:title="@string/pref_title_manage_sim_messages" />
+ <Preference
+ android:key="pref_key_manage_sim_messages_slot1"
+ android:title="@string/pref_title_manage_sim_messages_slot1" />
+ <Preference
+ android:key="pref_key_manage_sim_messages_slot2"
+ android:title="@string/pref_title_manage_sim_messages_slot2" />
+
<!-- QuickMessage settings -->
<PreferenceCategory android:title="@string/pref_quickmessage">
diff --git a/res/xml-v23/preferences_application.xml b/res/xml-v23/preferences_application.xml
index 95ba3c3..e198038 100644
--- a/res/xml-v23/preferences_application.xml
+++ b/res/xml-v23/preferences_application.xml
@@ -147,6 +147,16 @@
android:title="@string/mms_delete_pref_title" />
</PreferenceCategory>
+ <Preference
+ android:key="pref_key_manage_sim_messages"
+ android:title="@string/pref_title_manage_sim_messages" />
+ <Preference
+ android:key="pref_key_manage_sim_messages_slot1"
+ android:title="@string/pref_title_manage_sim_messages_slot1" />
+ <Preference
+ android:key="pref_key_manage_sim_messages_slot2"
+ android:title="@string/pref_title_manage_sim_messages_slot2" />
+
<!-- QuickMessage settings -->
<PreferenceCategory android:title="@string/pref_quickmessage">
diff --git a/res/xml/preferences_application.xml b/res/xml/preferences_application.xml
index 64bc7e2..279ee9a 100644
--- a/res/xml/preferences_application.xml
+++ b/res/xml/preferences_application.xml
@@ -145,6 +145,15 @@
android:title="@string/mms_delete_pref_title" />
</PreferenceCategory>
+ <Preference
+ android:key="pref_key_manage_sim_messages"
+ android:title="@string/pref_title_manage_sim_messages" />
+ <Preference
+ android:key="pref_key_manage_sim_messages_slot1"
+ android:title="@string/pref_title_manage_sim_messages_slot1" />
+ <Preference
+ android:key="pref_key_manage_sim_messages_slot2"
+ android:title="@string/pref_title_manage_sim_messages_slot2" />
<!-- QuickMessage settings -->
<PreferenceCategory android:title="@string/pref_quickmessage">
diff --git a/src/com/android/messaging/datamodel/data/ConversationMessageData.java b/src/com/android/messaging/datamodel/data/ConversationMessageData.java
index 19e1b97..9a160e9 100644
--- a/src/com/android/messaging/datamodel/data/ConversationMessageData.java
+++ b/src/com/android/messaging/datamodel/data/ConversationMessageData.java
@@ -19,6 +19,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.ContactsContract;
+import android.telephony.SmsManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -134,6 +135,25 @@ public class ConversationMessageData {
}
}
+ public void bindToSimMessages(Cursor cursor) {
+ mMessageId = String.valueOf(cursor.getInt(SimMessageData.INDEX_INDEX_ON_ICC));
+ int IccStatus = cursor.getInt(SimMessageData.INDEX_STATUS);
+ if (IccStatus == SmsManager.STATUS_ON_ICC_SENT) {
+ mStatus = MessageData.BUGLE_STATUS_OUTGOING_COMPLETE;
+ mSentTimestamp = cursor.getLong(SimMessageData.INDEX_DATE);
+ } else {
+ mStatus = MessageData.BUGLE_STATUS_INCOMING_COMPLETE;
+ mReceivedTimestamp = cursor.getLong(SimMessageData.INDEX_DATE);
+ }
+ mSenderDisplayDestination = cursor.getString(SimMessageData.INDEX_ADDRESS);
+ mPartsCount = 1;
+ mParts = new ArrayList<MessagePartData>();
+ mParts.add(new MessagePartData(cursor.getString(SimMessageData.INDEX_BODY)));
+ mSeen = true;
+ mRead = true;
+ mProtocol = MessageData.PROTOCOL_SMS;
+ }
+
private boolean canClusterWithMessage(final Cursor cursor) {
final String otherParticipantId = cursor.getString(INDEX_PARTICIPANT_ID);
if (!TextUtils.equals(getParticipantId(), otherParticipantId)) {
diff --git a/src/com/android/messaging/datamodel/data/SimMessageData.java b/src/com/android/messaging/datamodel/data/SimMessageData.java
new file mode 100644
index 0000000..937b0e2
--- /dev/null
+++ b/src/com/android/messaging/datamodel/data/SimMessageData.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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.datamodel.data;
+
+public class SimMessageData {
+
+ public static final int INDEX_SERVICE_CENTER_ADDRESS = 0;
+ public static final int INDEX_ADDRESS = 1;
+ public static final int INDEX_MESSAGE_CLASS = 2;
+ public static final int INDEX_BODY = 3;
+ public static final int INDEX_DATE = 4;
+ public static final int INDEX_STATUS = 5;
+ public static final int INDEX_INDEX_ON_ICC = 6;
+ public static final int INDEX_IS_STATUS_REPORT = 7;
+ public static final int INDEX_TRANSPORT_TYPE = 8;
+ public static final int INDEX_TYPE = 9;
+ public static final int INDEX_LOCKED = 10;
+ public static final int INDEX_ERROR_CODE = 11;
+ public static final int INDEX_ID = 12;
+ public static final int INDEX_SUB_ID = 13;
+}
diff --git a/src/com/android/messaging/sms/SimMessagesUtils.java b/src/com/android/messaging/sms/SimMessagesUtils.java
new file mode 100644
index 0000000..f7be881
--- /dev/null
+++ b/src/com/android/messaging/sms/SimMessagesUtils.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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.sms;
+
+import android.content.Context;
+import android.net.Uri;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.internal.telephony.EncodeException;
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.cdma.sms.BearerData;
+import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
+import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.messaging.datamodel.data.ConversationMessageData;
+import com.android.messaging.datamodel.data.ConversationParticipantsData;
+import com.android.messaging.R;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import static android.telephony.SmsMessage.ENCODING_16BIT;
+import static android.telephony.SmsMessage.ENCODING_7BIT;
+import static android.telephony.SmsMessage.ENCODING_UNKNOWN;
+import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
+import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
+
+public class SimMessagesUtils {
+
+ private static final String TAG = SimMessagesUtils.class.getSimpleName();
+ public static final int SUB_INVALID = -1; // for single card product
+ public static final int SUB1 = 0; // for DSDS product of slot one
+ public static final int SUB2 = 1; // for DSDS product of slot two
+
+ public static final Uri ICC_URI = Uri.parse("content://sms/icc");
+ public static final Uri ICC1_URI = Uri.parse("content://sms/icc1");
+ public static final Uri ICC2_URI = Uri.parse("content://sms/icc2");
+ private static final int TIMESTAMP_LENGTH = 7; // See TS 23.040 9.2.3.11
+ public static String WAPPUSH = "Browser Information"; // Wap push key
+
+ /**
+ * Return the icc uri according to subscription
+ */
+ public static Uri getIccUriBySlot(int slot) {
+ switch (slot) {
+ case SUB1:
+ return ICC1_URI;
+ case SUB2:
+ return ICC2_URI;
+ default:
+ return ICC_URI;
+ }
+ }
+
+ public static boolean isMultiSimEnabledMms() {
+ return TelephonyManager.getDefault().isMultiSimEnabled();
+ }
+
+ /**
+ * Return whether it has card no matter in DSDS or not
+ */
+ public static boolean hasIccCard() {
+ return TelephonyManager.getDefault().hasIccCard();
+ }
+
+ /**
+ * Return whether the card in the given slot is activated
+ */
+ public static boolean isIccCardActivated(int slot) {
+ TelephonyManager tm = TelephonyManager.getDefault();
+ final int simState = tm.getSimState(slot);
+ return (simState != TelephonyManager.SIM_STATE_ABSENT)
+ && (simState != TelephonyManager.SIM_STATE_UNKNOWN);
+ }
+
+ public static boolean copyToSim(ConversationMessageData messageData,
+ ConversationParticipantsData participants, int subId) {
+ String address;
+ address = (participants.getOtherParticipant() == null) ?
+ null :
+ participants.getOtherParticipant().getDisplayDestination();
+ if(TextUtils.isEmpty(address)) {
+ return false;
+ }
+ if (SimMessagesUtils.isWapPushNumber(address)) {
+ String[] number = address.split(":");
+ address = number[0];
+ }
+
+ String text = messageData.getText();
+ if (TextUtils.isEmpty(text)) {
+ return false;
+ }
+ long timestamp = messageData.getReceivedTimeStamp() != 0 ?
+ messageData.getReceivedTimeStamp() :
+ System.currentTimeMillis();
+
+ SmsManager sm = SmsManager.getDefault();
+ ArrayList<String> messages = sm.divideMessage(text);
+
+ boolean ret = true;
+ for (String message : messages) {
+ byte pdu[] = null;
+ int status;
+ if (messageData.getIsIncoming()) {
+ pdu = SimMessagesUtils.getDeliveryPdu(null, address,
+ message, timestamp, subId);
+ status = SmsManager.STATUS_ON_ICC_READ;
+ } else {
+ pdu = SmsMessage.getSubmitPdu(null, address, message,
+ false, subId).encodedMessage;
+ status = SmsManager.STATUS_ON_ICC_SENT;
+ }
+ ret &= TelephonyManager.getDefault().isMultiSimEnabled()
+ ? SmsManager.getSmsManagerForSubscriptionId(subId)
+ .copyMessageToIcc(null, pdu, status)
+ : sm.copyMessageToIcc(null, pdu, status);
+ if (!ret) {
+ break;
+ }
+ }
+ return ret;
+ }
+
+ private static boolean isCDMAPhone(int subscription) {
+ int activePhone = isMultiSimEnabledMms()
+ ? TelephonyManager.getDefault().getCurrentPhoneType(subscription)
+ : TelephonyManager.getDefault().getPhoneType();
+ return activePhone == TelephonyManager.PHONE_TYPE_CDMA;
+ }
+
+ public static byte[] getDeliveryPdu(String scAddress, String destinationAddress, String message,
+ long date, int subscription) {
+ if (isCDMAPhone(subscription)) {
+ return getCDMADeliveryPdu(scAddress, destinationAddress, message, date);
+ } else {
+ return getDeliveryPdu(scAddress, destinationAddress, message, date, null,
+ ENCODING_UNKNOWN);
+ }
+ }
+
+ public static byte[] getCDMADeliveryPdu(String scAddress, String destinationAddress,
+ String message, long date) {
+ // Perform null parameter checks.
+ if (message == null || destinationAddress == null) {
+ Log.d(TAG, "getCDMADeliveryPdu,message =null");
+ return null;
+ }
+
+ // according to submit pdu encoding as written in privateGetSubmitPdu
+
+ // MTI = SMS-DELIVERY, UDHI = header != null
+ byte[] header = null;
+ byte mtiByte = (byte) (0x00 | (header != null ? 0x40 : 0x00));
+ ByteArrayOutputStream headerStream = getDeliveryPduHeader(destinationAddress, mtiByte);
+
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream(MAX_USER_DATA_BYTES + 40);
+
+ DataOutputStream dos = new DataOutputStream(byteStream);
+ // int status,Status of message. See TS 27.005 3.1, "<stat>"
+
+ /* 0 = "REC UNREAD" */
+ /* 1 = "REC READ" */
+ /* 2 = "STO UNSENT" */
+ /* 3 = "STO SENT" */
+
+ try {
+ // int uTeleserviceID;
+ int uTeleserviceID = 0; //.TELESERVICE_CT_WAP;// int
+ dos.writeInt(uTeleserviceID);
+
+ // unsigned char bIsServicePresent
+ byte bIsServicePresent = 0;// byte
+ dos.writeInt(bIsServicePresent);
+
+ // uServicecategory
+ int uServicecategory = 0;// int
+ dos.writeInt(uServicecategory);
+
+ // RIL_CDMA_SMS_Address
+ // digit_mode
+ // number_mode
+ // number_type
+ // number_plan
+ // number_of_digits
+ // digits[]
+ CdmaSmsAddress destAddr = CdmaSmsAddress.parse(PhoneNumberUtils
+ .cdmaCheckAndProcessPlusCode(destinationAddress));
+ if (destAddr == null)
+ return null;
+ dos.writeByte(destAddr.digitMode);// int
+ dos.writeByte(destAddr.numberMode);// int
+ dos.writeByte(destAddr.ton);// int
+ dos.writeByte(destAddr.numberPlan);// int
+ dos.writeByte(destAddr.numberOfDigits);// byte
+ dos.write(destAddr.origBytes, 0, destAddr.origBytes.length); // digits
+
+ // RIL_CDMA_SMS_Subaddress
+ // Subaddress is not supported.
+ dos.writeByte(0); // subaddressType int
+ dos.writeByte(0); // subaddr_odd byte
+ dos.writeByte(0); // subaddr_nbr_of_digits byte
+
+ SmsHeader smsHeader = new SmsHeader().fromByteArray(headerStream.toByteArray());
+ UserData uData = new UserData();
+ uData.payloadStr = message;
+ // uData.userDataHeader = smsHeader;
+ uData.msgEncodingSet = true;
+ uData.msgEncoding = UserData.ENCODING_UNICODE_16;
+
+ BearerData bearerData = new BearerData();
+ bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+
+ bearerData.deliveryAckReq = false;
+ bearerData.userAckReq = false;
+ bearerData.readAckReq = false;
+ bearerData.reportReq = false;
+
+ bearerData.userData = uData;
+
+ byte[] encodedBearerData = BearerData.encode(bearerData);
+ if (null != encodedBearerData) {
+ // bearer data len
+ dos.writeByte(encodedBearerData.length);// int
+ Log.d(TAG, "encodedBearerData length=" + encodedBearerData.length);
+
+ // aBearerData
+ dos.write(encodedBearerData, 0, encodedBearerData.length);
+ } else {
+ dos.writeByte(0);
+ }
+
+ } catch (IOException e) {
+ Log.e(TAG, "Error writing dos", e);
+ } finally {
+ try {
+ if (null != byteStream) {
+ byteStream.close();
+ }
+
+ if (null != dos) {
+ dos.close();
+ }
+
+ if (null != headerStream) {
+ headerStream.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error close dos", e);
+ }
+ }
+
+ return byteStream.toByteArray();
+ }
+
+ /**
+ * Generate a Delivery PDU byte array. see getSubmitPdu for reference.
+ */
+ public static byte[] getDeliveryPdu(String scAddress, String destinationAddress, String message,
+ long date, byte[] header, int encoding) {
+ // Perform null parameter checks.
+ if (message == null || destinationAddress == null) {
+ return null;
+ }
+
+ // MTI = SMS-DELIVERY, UDHI = header != null
+ byte mtiByte = (byte)(0x00 | (header != null ? 0x40 : 0x00));
+ ByteArrayOutputStream bo = getDeliveryPduHeader(destinationAddress, mtiByte);
+ // User Data (and length)
+ byte[] userData;
+ if (encoding == ENCODING_UNKNOWN) {
+ // First, try encoding it with the GSM alphabet
+ encoding = ENCODING_7BIT;
+ }
+ try {
+ if (encoding == ENCODING_7BIT) {
+ userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header, 0, 0);
+ } else { //assume UCS-2
+ try {
+ userData = encodeUCS2(message, header);
+ } catch (UnsupportedEncodingException uex) {
+ Log.e("GSM", "Implausible UnsupportedEncodingException ",
+ uex);
+ return null;
+ }
+ }
+ } catch (EncodeException ex) {
+ // Encoding to the 7-bit alphabet failed. Let's see if we can
+ // encode it as a UCS-2 encoded message
+ try {
+ userData = encodeUCS2(message, header);
+ encoding = ENCODING_16BIT;
+ } catch (UnsupportedEncodingException uex) {
+ Log.e("GSM", "Implausible UnsupportedEncodingException ",
+ uex);
+ return null;
+ }
+ }
+
+ if (encoding == ENCODING_7BIT) {
+ if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
+ // Message too long
+ return null;
+ }
+ bo.write(0x00);
+ } else { //assume UCS-2
+ if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
+ // Message too long
+ return null;
+ }
+ // TP-Data-Coding-Scheme
+ // Class 3, UCS-2 encoding, uncompressed
+ bo.write(0x0b);
+ }
+ byte[] timestamp = getTimestamp(date);
+ bo.write(timestamp, 0, timestamp.length);
+
+ bo.write(userData, 0, userData.length);
+ return bo.toByteArray();
+ }
+
+ private static ByteArrayOutputStream getDeliveryPduHeader(
+ String destinationAddress, byte mtiByte) {
+ ByteArrayOutputStream bo = new ByteArrayOutputStream(
+ MAX_USER_DATA_BYTES + 40);
+ bo.write(mtiByte);
+
+ byte[] daBytes;
+ daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress);
+
+ // destination address length in BCD digits, ignoring TON byte and pad
+ // TODO Should be better.
+ bo.write((daBytes.length - 1) * 2
+ - ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
+
+ // destination address
+ bo.write(daBytes, 0, daBytes.length);
+
+ // TP-Protocol-Identifier
+ bo.write(0);
+ return bo;
+ }
+
+ private static byte[] getTimestamp(long time) {
+ // See TS 23.040 9.2.3.11
+ byte[] timestamp = new byte[TIMESTAMP_LENGTH];
+ SimpleDateFormat sdf = new SimpleDateFormat("yyMMddkkmmss:Z", Locale.US);
+ String[] date = sdf.format(time).split(":");
+ // generate timezone value
+ String timezone = date[date.length - 1];
+ String signMark = timezone.substring(0, 1);
+ int hour = Integer.parseInt(timezone.substring(1, 3));
+ int min = Integer.parseInt(timezone.substring(3));
+ int timezoneValue = hour * 4 + min / 15;
+ // append timezone value to date[0] (time string)
+ String timestampStr = date[0] + timezoneValue;
+
+ int digitCount = 0;
+ for (int i = 0; i < timestampStr.length(); i++) {
+ char c = timestampStr.charAt(i);
+ int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
+ timestamp[(digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
+ digitCount++;
+ }
+
+ if (signMark.equals("-")) {
+ timestamp[timestamp.length - 1] = (byte) (timestamp[timestamp.length - 1] | 0x08);
+ }
+
+ return timestamp;
+ }
+
+ private static byte[] encodeUCS2(String message, byte[] header)
+ throws UnsupportedEncodingException {
+ byte[] userData, textPart;
+ textPart = message.getBytes("utf-16be");
+
+ if (header != null) {
+ // Need 1 byte for UDHL
+ userData = new byte[header.length + textPart.length + 1];
+
+ userData[0] = (byte)header.length;
+ System.arraycopy(header, 0, userData, 1, header.length);
+ System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
+ }
+ else {
+ userData = textPart;
+ }
+ byte[] ret = new byte[userData.length+1];
+ ret[0] = (byte) (userData.length & 0xff );
+ System.arraycopy(userData, 0, ret, 1, userData.length);
+ return ret;
+ }
+
+ private static int charToBCD(char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else {
+ throw new RuntimeException ("invalid char for BCD " + c);
+ }
+ }
+
+ /**
+ * Returns true if the address passed in is a Browser wap push MMS address.
+ */
+ public static boolean isWapPushNumber(String address) {
+ if (TextUtils.isEmpty(address)) {
+ return false;
+ } else {
+ return address.contains(WAPPUSH);
+ }
+ }
+
+ /**
+ * Return the sim name of subscription.
+ */
+ public static String getMultiSimName(Context context, int slot) {
+ if (slot >= TelephonyManager.getDefault().getPhoneCount() || slot < 0) {
+ return null;
+ }
+ //String multiSimName = Settings.System.getString(context.getContentResolver(),
+ // MULTI_SIM_NAME + (subscription + 1));
+ //if (multiSimName == null) {
+ if (slot == SUB1) {
+ return context.getString(R.string.slot1);
+ } else if (slot == SUB2) {
+ return context.getString(R.string.slot2);
+ }
+ //}
+ return context.getString(R.string.slot1);
+ }
+
+ /**
+ * Return the activated card number
+ */
+ public static int getActivatedIccCardCount() {
+ TelephonyManager tm = TelephonyManager.getDefault();
+ int phoneCount = tm.getPhoneCount();
+ int count = 0;
+ for (int i = 0; i < phoneCount; i++) {
+ if (isIccCardActivated(i)) {
+ count++;
+ }
+ }
+ return count;
+ }
+}
diff --git a/src/com/android/messaging/ui/ManageSimMessages.java b/src/com/android/messaging/ui/ManageSimMessages.java
new file mode 100644
index 0000000..f658300
--- /dev/null
+++ b/src/com/android/messaging/ui/ManageSimMessages.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 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;
+
+import android.app.AlertDialog;
+import android.content.AsyncQueryHandler;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SqliteWrapper;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v7.app.ActionBar;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
+
+import com.android.messaging.R;
+import com.android.messaging.datamodel.data.MessagePartData;
+import com.android.messaging.datamodel.data.SubscriptionListData;
+import com.android.messaging.sms.SimMessagesUtils;
+import com.android.messaging.ui.conversation.ConversationMessageAdapter;
+import com.android.messaging.ui.conversation.ConversationMessageView;
+import com.android.messaging.util.UiUtils;
+
+/**
+ * Displays a list of the SMS messages stored on the ICC.
+ */
+public class ManageSimMessages extends BugleActionBarActivity
+ implements ConversationMessageView.ConversationMessageViewHost,
+ View.OnCreateContextMenuListener {
+ private static final String TAG = ManageSimMessages.class.getSimpleName();
+
+ private static final int SHOW_LIST = 0;
+ private static final int SHOW_EMPTY = 1;
+ private static final int SHOW_BUSY = 2;
+ private int mState;
+ private int mSlot;
+ private int mSubscription;
+
+ private Uri mIccUri;
+ private ContentResolver mContentResolver;
+ private Cursor mCursor = null;
+ private RecyclerView mSimList;
+ private TextView mMessage;
+ private ConversationMessageAdapter mListAdapter = null;
+ private AsyncQueryHandler mQueryHandler = null;
+ private boolean mIsQuery = false;
+ private ConversationMessageView mSelectedMessage = null;
+ public static final int TYPE_INBOX = 1;
+
+ private final ContentObserver simChangeObserver =
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfUpdate) {
+ refreshMessageList();
+ }
+ };
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
+ refreshMessageList();
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mContentResolver = getContentResolver();
+ mQueryHandler = new QueryHandler(mContentResolver, this);
+ setContentView(R.layout.sim_list);
+ mSimList = (RecyclerView) findViewById(android.R.id.list);
+ final LinearLayoutManager manager = new LinearLayoutManager(this);
+ manager.setStackFromEnd(false);
+ manager.setReverseLayout(false);
+ mSimList.setHasFixedSize(true);
+ mSimList.setLayoutManager(manager);
+ mMessage = (TextView) findViewById(R.id.empty_message);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ registerReceiver(mReceiver, filter);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ init();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ setIntent(intent);
+
+ init();
+ }
+
+ private void init() {
+ mSlot = getIntent().getIntExtra(PhoneConstants.PHONE_KEY, SimMessagesUtils.SUB_INVALID);
+
+ mSubscription = SimMessagesUtils.SUB_INVALID;
+ int[] subIds = SubscriptionManager.getSubId(mSlot);
+ if (subIds != null && subIds.length > 0) {
+ mSubscription = subIds[0];
+ }
+
+ mIccUri = SimMessagesUtils.getIccUriBySlot(mSlot);
+ updateState(SHOW_BUSY);
+ startQuery();
+ }
+
+ @Override
+ public boolean onAttachmentClick(ConversationMessageView view, MessagePartData attachment,
+ Rect imageBounds, boolean longPress) {
+ return false;
+ }
+
+ @Override
+ public SubscriptionListData.SubscriptionListEntry getSubscriptionEntryForSelfParticipant(
+ String selfParticipantId, boolean excludeDefault) {
+ return null;
+ }
+
+ private class QueryHandler extends AsyncQueryHandler {
+
+ public QueryHandler(
+ ContentResolver contentResolver, ManageSimMessages parent) {
+ super(contentResolver);
+ }
+
+ @Override
+ protected void onQueryComplete(
+ int token, Object cookie, Cursor cursor) {
+ if (mCursor != null) {
+ stopManagingCursor(mCursor);
+ }
+ mCursor = cursor;
+ if (mCursor != null) {
+ if (!mCursor.moveToFirst()) {
+ // Let user know the SIM is empty
+ updateState(SHOW_EMPTY);
+ } else if (mListAdapter == null) {
+ mListAdapter = new ConversationMessageAdapter(
+ ManageSimMessages.this, mCursor, ManageSimMessages.this, null,
+ onMessageListItemClick, onMessageListItemLongClick, true);
+ mSimList.setAdapter(mListAdapter);
+ updateState(SHOW_LIST);
+ } else {
+ mListAdapter.changeCursor(mCursor);
+ updateState(SHOW_LIST);
+ }
+ startManagingCursor(mCursor);
+ } else {
+ // Let user know the SIM is empty
+ updateState(SHOW_EMPTY);
+ }
+ mIsQuery = false;
+ }
+ }
+
+ private void startQuery() {
+ try {
+ if (mIsQuery) {
+ return;
+ }
+ mIsQuery = true;
+ mQueryHandler.startQuery(0, null, mIccUri, null, null, null, null);
+ } catch (SQLiteException e) {
+ SqliteWrapper.checkSQLiteException(this, e);
+ }
+ }
+
+ private void refreshMessageList() {
+ updateState(SHOW_BUSY);
+ startQuery();
+ }
+
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ registerSimChangeObserver();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mContentResolver.unregisterContentObserver(simChangeObserver);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ if (mSelectedMessage == null) {
+ finish();
+ } else {
+ dismissActionMode();
+ }
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onDestroy() {
+ unregisterReceiver(mReceiver);
+ super.onDestroy();
+ }
+
+ @Override
+ protected void updateActionBar(ActionBar actionBar) {
+ super.updateActionBar(actionBar);
+ //The Action Mode menu changes the action bar completely. Need to reset it!
+ updateActionAndStatusBarColor(getSupportActionBar());
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setHomeAsUpIndicator(0);
+ actionBar.setDisplayShowTitleEnabled(true);
+ }
+
+ private void updateActionAndStatusBarColor(final ActionBar actionBar) {
+ final int themeColor = ConversationDrawables.get().getConversationThemeColor();
+ actionBar.setBackgroundDrawable(new ColorDrawable(themeColor));
+ UiUtils.setStatusBarColor(this, themeColor);
+ }
+
+ private void registerSimChangeObserver() {
+ mContentResolver.registerContentObserver(
+ mIccUri, true, simChangeObserver);
+ }
+
+ private void updateState(int state) {
+ if (mState == state) {
+ return;
+ }
+
+ mState = state;
+ switch (state) {
+ case SHOW_LIST:
+ mSimList.setVisibility(View.VISIBLE);
+ mMessage.setVisibility(View.GONE);
+ setTitle(getString(R.string.sim_manage_messages_title));
+ setProgressBarIndeterminateVisibility(false);
+ mSimList.requestFocus();
+ break;
+ case SHOW_EMPTY:
+ mSimList.setVisibility(View.GONE);
+ mMessage.setVisibility(View.VISIBLE);
+ setTitle(getString(R.string.sim_manage_messages_title));
+ setProgressBarIndeterminateVisibility(false);
+ break;
+ case SHOW_BUSY:
+ mSimList.setVisibility(View.GONE);
+ mMessage.setVisibility(View.GONE);
+ setTitle(getString(R.string.refreshing));
+ setProgressBarIndeterminateVisibility(true);
+ break;
+ default:
+ Log.e(TAG, "Invalid State");
+ }
+ }
+
+ public Context getContext() {
+ return ManageSimMessages.this;
+ }
+
+ private void selectMessage(final ConversationMessageView messageView) {
+ mSelectedMessage = messageView;
+ if (mSelectedMessage == null) {
+ mListAdapter.setSelectedMessage(null);
+ dismissActionMode();
+ return;
+ }
+ mListAdapter.setSelectedMessage(messageView.getData().getMessageId());
+ startActionMode(mActionModeCallback);
+ }
+
+ private void confirmDeleteDialog(DialogInterface.OnClickListener listener) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setTitle(R.string.confirm_dialog_title);
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setCancelable(true);
+ builder.setPositiveButton(R.string.yes, listener);
+ builder.setNegativeButton(R.string.no, listener);
+ builder.setMessage(R.string.confirm_delete_selected_messages);
+ builder.show();
+ }
+
+ private void deleteMessageFromSim() {
+ if (mSelectedMessage == null) {
+ return;
+ }
+ String messageIndexString =
+ mSelectedMessage.getData().getMessageId();
+ if (TextUtils.isEmpty(messageIndexString)) {
+ return;
+ }
+ Uri simUri = mIccUri.buildUpon().appendPath(messageIndexString).build();
+ SqliteWrapper.delete(this, mContentResolver, simUri, null, null);
+ }
+
+ View.OnClickListener onMessageListItemClick = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ //Do Nothing
+ }
+ };
+
+ View.OnLongClickListener onMessageListItemLongClick = new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ selectMessage((ConversationMessageView) view);
+ return true;
+ }
+ };
+
+ private class DeleteConfirmListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ switch (whichButton) {
+ case DialogInterface.BUTTON_POSITIVE:
+ deleteMessageFromSim();
+ break;
+ }
+ dismissActionMode();
+ }
+ }
+
+ private final ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ if (mSelectedMessage != null) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.sim_msg_multi_select_menu, menu);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.delete:
+ confirmDeleteDialog(new DeleteConfirmListener());
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ selectMessage(null);
+ }
+ };
+}
diff --git a/src/com/android/messaging/ui/appsettings/ApplicationSettingsActivity.java b/src/com/android/messaging/ui/appsettings/ApplicationSettingsActivity.java
index b91cc6c..ae5bbf3 100644
--- a/src/com/android/messaging/ui/appsettings/ApplicationSettingsActivity.java
+++ b/src/com/android/messaging/ui/appsettings/ApplicationSettingsActivity.java
@@ -37,10 +37,13 @@ import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
+import com.android.internal.telephony.PhoneConstants;
import com.android.messaging.R;
import com.android.messaging.sms.MmsConfig;
+import com.android.messaging.sms.SimMessagesUtils;
import com.android.messaging.ui.BugleActionBarActivity;
import com.android.messaging.ui.LicenseActivity;
+import com.android.messaging.ui.ManageSimMessages;
import com.android.messaging.ui.NumberPickerDialog;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.util.BuglePrefs;
@@ -109,6 +112,9 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
private ListPreference mSmsValidityCard2Pref;
private Preference mSmsLimitPref;
private Preference mMmsLimitPref;
+ private Preference mManageSimPref;
+ private Preference mManageSim1Pref;
+ private Preference mManageSim2Pref;
public ApplicationSettingsFragment() {
@@ -139,6 +145,9 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
mSmsValidityCard2Pref = (ListPreference) findPreference("pref_key_sms_validity_period_slot2");
mSmsLimitPref = findPreference("sms_delete_limit_pref_key");
mMmsLimitPref = findPreference("mms_delete_limit_pref_key");
+ mManageSimPref = findPreference("pref_key_manage_sim_messages");
+ mManageSim1Pref = findPreference("pref_key_manage_sim_messages_slot1");
+ mManageSim2Pref = findPreference("pref_key_manage_sim_messages_slot2");
if (getResources().getBoolean(R.bool.config_sms_validity)) {
if (PhoneUtils.getDefault().isMultiSimEnabledMms()) {
@@ -181,6 +190,7 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
}
setSmsDisplayLimit();
setMmsDisplayLimit();
+ updateSIMSMSPref();
}
@Override
@@ -189,28 +199,38 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
if (preference.getKey() == mSmsDisabledPrefKey ||
preference.getKey() == mSmsEnabledPrefKey) {
mIsSmsPreferenceClicked = true;
- } else if (getActivity() != null &&
- preference.getKey().equals(mSmsLimitPref.getKey())) {
+ } else if (getActivity() != null) {
+ if (preference.getKey().equals(mSmsLimitPref.getKey())) {
new NumberPickerDialog(getActivity(),
- mSmsLimitListener,
- PrefsUtils.getSMSMessagesPerThreadLimit(),
- MmsConfig.getMinMessageCountPerThread(),
- MmsConfig.getMaxMessageCountPerThread(),
- R.string.sms_delete_pref_title,
- R.string.pref_messages_to_save).show();
+ mSmsLimitListener,
+ PrefsUtils.getSMSMessagesPerThreadLimit(),
+ MmsConfig.getMinMessageCountPerThread(),
+ MmsConfig.getMaxMessageCountPerThread(),
+ R.string.sms_delete_pref_title,
+ R.string.pref_messages_to_save).show();
- } else if(getActivity() != null &&
- preference.getKey().equals(mMmsLimitPref.getKey())) {
+ } else if (preference.getKey().equals(mMmsLimitPref.getKey())) {
new NumberPickerDialog(getActivity(),
- mMmsLimitListener,
- PrefsUtils.getMMSMessagesPerThreadLimit(),
- MmsConfig.getMinMessageCountPerThread(),
- MmsConfig.getMaxMessageCountPerThread(),
- R.string.mms_delete_pref_title,
- R.string.pref_messages_to_save).show();
-
+ mMmsLimitListener,
+ PrefsUtils.getMMSMessagesPerThreadLimit(),
+ MmsConfig.getMinMessageCountPerThread(),
+ MmsConfig.getMaxMessageCountPerThread(),
+ R.string.mms_delete_pref_title,
+ R.string.pref_messages_to_save).show();
+
+ } else if (preference.getKey().equals(mManageSimPref.getKey())) {
+ startActivity(new Intent(getActivity(), ManageSimMessages.class));
+ } else if (preference.getKey().equals(mManageSim1Pref.getKey())) {
+ Intent intent = new Intent(getActivity(), ManageSimMessages.class);
+ intent.putExtra(PhoneConstants.PHONE_KEY, SimMessagesUtils.SUB1);
+ startActivity(intent);
+ } else if (preference.getKey().equals(mManageSim2Pref.getKey())) {
+ Intent intent = new Intent(getActivity(), ManageSimMessages.class);
+ intent.putExtra(PhoneConstants.PHONE_KEY, SimMessagesUtils.SUB2);
+ startActivity(intent);
+ }
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
@@ -330,6 +350,24 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
PrefsUtils.getMMSMessagesPerThreadLimit()));
}
+ private void updateSIMSMSPref() {
+ if (SimMessagesUtils.isMultiSimEnabledMms()) {
+ if (!SimMessagesUtils.isIccCardActivated(SimMessagesUtils.SUB1)) {
+ mManageSim1Pref.setEnabled(false);
+ }
+ if (!SimMessagesUtils.isIccCardActivated(SimMessagesUtils.SUB2)) {
+ mManageSim2Pref.setEnabled(false);
+ }
+ getPreferenceScreen().removePreference(mManageSimPref);
+ } else {
+ if (!SimMessagesUtils.hasIccCard()) {
+ mManageSimPref.setEnabled(false);
+ }
+ getPreferenceScreen().removePreference(mManageSim1Pref);
+ getPreferenceScreen().removePreference(mManageSim2Pref);
+ }
+ }
+
NumberPickerDialog.OnNumberSetListener mSmsLimitListener =
new NumberPickerDialog.OnNumberSetListener() {
public void onNumberSet(int limit) {
diff --git a/src/com/android/messaging/ui/conversation/ConversationFragment.java b/src/com/android/messaging/ui/conversation/ConversationFragment.java
index 4020eed..0bfd644 100644
--- a/src/com/android/messaging/ui/conversation/ConversationFragment.java
+++ b/src/com/android/messaging/ui/conversation/ConversationFragment.java
@@ -43,6 +43,7 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Parcelable;
+import android.provider.Telephony;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.text.BidiFormatter;
import android.support.v4.text.TextDirectionHeuristicsCompat;
@@ -51,7 +52,12 @@ import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.Log;
import android.view.ActionMode;
import android.view.Display;
import android.view.LayoutInflater;
@@ -63,6 +69,7 @@ import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.TextView;
+import android.widget.Toast;
import com.android.messaging.BugleApplication;
import com.android.messaging.R;
import com.android.messaging.datamodel.DataModel;
@@ -81,6 +88,7 @@ import com.android.messaging.datamodel.data.MessageData;
import com.android.messaging.datamodel.data.MessagePartData;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.datamodel.data.SubscriptionListData.SubscriptionListEntry;
+import com.android.messaging.sms.SimMessagesUtils;
import com.android.messaging.ui.AttachmentPreview;
import com.android.messaging.ui.BugleActionBarActivity;
import com.android.messaging.ui.ConversationDrawables;
@@ -373,6 +381,16 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
UIIntents.get().launchForwardMessageActivity(getActivity(), message);
mHost.dismissActionMode();
return true;
+ case R.id.copy_to_sim:
+ if (data != null && mBinding.getData().getParticipants() != null ) {
+ if(SimMessagesUtils.getActivatedIccCardCount() > 1) {
+ showSimSelectDialog(data);
+ } else {
+ copyToSim(data, SubscriptionManager.getDefaultSmsSubId());
+ }
+ }
+ mHost.dismissActionMode();
+ return true;
}
return false;
}
@@ -1434,6 +1452,51 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
((BugleActionBarActivity) activity).supportInvalidateOptionsMenu();
}
+ private void copyToSim(ConversationMessageData data, int subId) {
+ boolean success = SimMessagesUtils
+ .copyToSim(data, mBinding.getData().getParticipants(), subId);
+ CharSequence copyToSimStatus = success ?
+ getResources().getText(R.string.copy_to_phone_success) :
+ getResources().getText(R.string.copy_to_sim_fail);
+ Toast.makeText(getActivity(), copyToSimStatus.toString(),
+ Toast.LENGTH_SHORT).show();
+ }
+
+ private void showSimSelectDialog(ConversationMessageData data) {
+ String[] items = new String[TelephonyManager.getDefault()
+ .getPhoneCount()];
+ for (int i = 0; i < items.length; i++) {
+ items[i] = SimMessagesUtils.getMultiSimName(
+ getActivity(), i);
+ }
+ CopyToSimSelectListener listener = new CopyToSimSelectListener(
+ data);
+ new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.copy_to_sim)
+ .setPositiveButton(android.R.string.ok, listener)
+ .setSingleChoiceItems(items, 0, listener)
+ .setCancelable(true).show();
+ }
+
+ private class CopyToSimSelectListener implements DialogInterface.OnClickListener {
+ private ConversationMessageData messageData;
+ private int slot;
+
+ public CopyToSimSelectListener(ConversationMessageData messageData) {
+ super();
+ this.messageData = messageData;
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (which >= 0) {
+ slot = which;
+ } else if (which == DialogInterface.BUTTON_POSITIVE) {
+ int[] subId = SubscriptionManager.getSubId(slot);
+ copyToSim(messageData, subId[0]);
+ }
+ }
+ }
+
@Override
public void setOptionsMenuVisibility(final boolean visible) {
setHasOptionsMenu(visible);
diff --git a/src/com/android/messaging/ui/conversation/ConversationMessageAdapter.java b/src/com/android/messaging/ui/conversation/ConversationMessageAdapter.java
index 2748fff..3d50252 100644
--- a/src/com/android/messaging/ui/conversation/ConversationMessageAdapter.java
+++ b/src/com/android/messaging/ui/conversation/ConversationMessageAdapter.java
@@ -45,6 +45,7 @@ public class ConversationMessageAdapter extends
private final View.OnLongClickListener mViewLongClickListener;
private boolean mOneOnOne;
private String mSelectedMessageId;
+ private boolean isSmsMessage;
public ConversationMessageAdapter(final Context context, final Cursor cursor,
final ConversationMessageViewHost host,
@@ -59,13 +60,28 @@ public class ConversationMessageAdapter extends
setHasStableIds(true);
}
+ public ConversationMessageAdapter(final Context context, final Cursor cursor,
+ final ConversationMessageViewHost host,
+ final AsyncImageViewDelayLoader imageViewDelayLoader,
+ final View.OnClickListener viewClickListener,
+ final View.OnLongClickListener longClickListener, boolean isSmsMessage) {
+ this(context, cursor, host, imageViewDelayLoader, viewClickListener, longClickListener);
+ this.isSmsMessage = isSmsMessage;
+ }
+
+
+
@Override
public void bindViewHolder(final ConversationMessageViewHolder holder,
final Context context, final Cursor cursor) {
Assert.isTrue(holder.mView instanceof ConversationMessageView);
final ConversationMessageView conversationMessageView =
(ConversationMessageView) holder.mView;
- conversationMessageView.bind(cursor, mOneOnOne, mSelectedMessageId);
+ if(isSmsMessage) {
+ conversationMessageView.bindToSimMessages(cursor, mSelectedMessageId);
+ } else {
+ conversationMessageView.bind(cursor, mOneOnOne, mSelectedMessageId);
+ }
}
@Override
diff --git a/src/com/android/messaging/ui/conversation/ConversationMessageBubbleView.java b/src/com/android/messaging/ui/conversation/ConversationMessageBubbleView.java
index d68cbde..395936d 100644
--- a/src/com/android/messaging/ui/conversation/ConversationMessageBubbleView.java
+++ b/src/com/android/messaging/ui/conversation/ConversationMessageBubbleView.java
@@ -95,11 +95,15 @@ public class ConversationMessageBubbleView extends LinearLayout {
}
}
- public void bind(QuickMessage quickMessage) {
+ public void bind() {
mShouldAnimateWidthChange = false;
mMorphedWidth = 0;
}
+ public void bind(QuickMessage quickMessage) {
+ bind();
+ }
+
public void kickOffMorphAnimation(final int oldWidth, final int newWidth) {
if (mAnimator != null) {
mAnimator.setIntValues(mRunningStartWidth, newWidth);
diff --git a/src/com/android/messaging/ui/conversation/ConversationMessageView.java b/src/com/android/messaging/ui/conversation/ConversationMessageView.java
index 4b61cdc..de50184 100644
--- a/src/com/android/messaging/ui/conversation/ConversationMessageView.java
+++ b/src/com/android/messaging/ui/conversation/ConversationMessageView.java
@@ -232,6 +232,25 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
contentTop + contentHeight);
}
+ public void bindToSimMessages(final Cursor cursor, final String selectedMessageId) {
+ mData.bindToSimMessages(cursor);
+ setSelected(TextUtils.equals(mData.getMessageId(), selectedMessageId));
+ // Update text and image content for the view.
+ updateViewContent();
+
+ // Update colors and layout parameters for the view.
+ updateViewAppearance();
+
+ updateContentDescription();
+
+ //Necessary to remove bubble width animation
+ mMessageBubble.bind();
+ //SIM Messages don't save timestamp for outgoing messages
+ if(!mData.getIsIncoming()) {
+ mStatusTextView.setVisibility(View.GONE);
+ }
+ }
+
/**
* Fills in the data associated with this view.
*