Snap for 8564071 from 682ab534f6f49f8a8b3fcc74ba172e0e2d9de97d to mainline-conscrypt-release
Change-Id: I77f1854609d177a4c9ba6960a0a711d9ce225cde
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..fd0ed55
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,104 @@
+// 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 {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ ],
+}
+
+soong_config_module_type {
+ name: "messaging_java_defaults",
+ module_type: "java_defaults",
+ config_namespace: "messaging",
+ bool_variables: ["build_variant_eng"],
+ properties: ["optimize"],
+}
+
+messaging_java_defaults {
+ name: "messaging_defaults",
+ soong_config_variables: {
+ build_variant_eng: {
+ optimize: {
+ proguard_flags_files: [
+ "proguard.flags",
+ "proguard-test.flags",
+ ],
+ },
+ conditions_default: {
+ optimize: {
+ proguard_flags_files: [
+ "proguard.flags",
+ "proguard-release.flags",
+ ],
+ }
+ },
+ },
+ },
+}
+
+
+android_app {
+ name: "messaging",
+
+ srcs: ["src/**/*.java"],
+
+ defaults: ["messaging_defaults"],
+
+ static_libs: [
+ "androidx.appcompat_appcompat",
+ "androidx.collection_collection",
+ "androidx.core_core",
+ "androidx.fragment_fragment",
+ "androidx.media_media",
+ "androidx.legacy_legacy-support-core-utils",
+ "androidx.legacy_legacy-support-core-ui",
+ "androidx.palette_palette",
+ "androidx.recyclerview_recyclerview",
+ "androidx.viewpager_viewpager",
+ "androidx.legacy_legacy-support-v13",
+ "colorpicker",
+ "libchips",
+ "libphotoviewer",
+ "androidx.annotation_annotation",
+ "android-common",
+ "android-common-framesequence",
+ "com.android.vcard",
+ "guava",
+ "libphonenumber",
+ ],
+
+ aaptflags: [
+ "--version-name",
+ "1.0.001",
+ "--version-code",
+ "10001040",
+ ],
+ required: [
+ "libframesequence",
+ "libgiftranscode",
+ ],
+ optimize: {
+ obfuscate: true,
+ optimize: true,
+ enabled: true,
+ },
+
+ certificate: "platform",
+
+ sdk_version: "current",
+
+ product_specific: true,
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index b6c2bb5..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,81 +0,0 @@
-# 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.
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_USE_AAPT2 := true
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- androidx.core_core \
- androidx.media_media \
- androidx.legacy_legacy-support-core-utils \
- androidx.legacy_legacy-support-core-ui \
- androidx.fragment_fragment \
- androidx.appcompat_appcompat \
- androidx.palette_palette \
- androidx.recyclerview_recyclerview \
- androidx.legacy_legacy-support-v13 \
- colorpicker \
- libchips \
- libphotoviewer
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.annotation_annotation \
- android-common \
- android-common-framesequence \
- com.android.vcard \
- guava \
- libphonenumber
-
-include $(LOCAL_PATH)/version.mk
-
-LOCAL_AAPT_FLAGS += --version-name "$(version_name_package)"
-LOCAL_AAPT_FLAGS += --version-code $(version_code_package)
-
-ifdef TARGET_BUILD_APPS
- LOCAL_JNI_SHARED_LIBRARIES := libframesequence libgiftranscode
-else
- LOCAL_REQUIRED_MODULES:= libframesequence libgiftranscode
-endif
-
-LOCAL_PROGUARD_ENABLED := obfuscation optimization
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-ifeq (eng,$(TARGET_BUILD_VARIANT))
- LOCAL_PROGUARD_FLAG_FILES += proguard-test.flags
-else
- LOCAL_PROGUARD_FLAG_FILES += proguard-release.flags
-endif
-
-LOCAL_PACKAGE_NAME := messaging
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PRODUCT_MODULE := true
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_APPS)
-
-LOCAL_COMPATIBILITY_SUITE := general-tests
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3468ac3..fffcdab 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -47,6 +47,7 @@
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Optional features -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
@@ -69,6 +70,7 @@
android:configChanges="orientation|screenSize|keyboardHidden"
android:screenOrientation="user"
android:label="@string/app_name"
+ android:exported="true"
android:theme="@style/BugleTheme.ConversationListActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -90,6 +92,7 @@
android:screenOrientation="user"
android:theme="@style/Invisible"
android:noHistory="true"
+ android:exported="true"
android:documentLaunchMode="always">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -98,12 +101,6 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
- <action android:name="android.intent.action.SENDTO" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
@@ -191,6 +188,7 @@
android:parentActivityName="com.android.messaging.ui.appsettings.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
+ android:exported="true"
android:value="com.android.messaging.ui.appsettings.SettingsActivity" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -206,6 +204,7 @@
android:screenOrientation="user"
android:theme="@style/BugleTheme.DialogActivity"
android:excludeFromRecents="true"
+ android:exported="true"
android:documentLaunchMode="always">
<intent-filter
android:label="@string/share_intent_label">
@@ -336,6 +335,7 @@
<!-- Registered with the highest possible priority (max_int) -->
<receiver android:name=".receiver.MmsWapPushReceiver"
android:enabled="false"
+ android:exported="true"
android:permission="android.permission.BROADCAST_WAP_PUSH">
<intent-filter android:priority="2147483647">
<action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
@@ -344,6 +344,7 @@
</receiver>
<receiver android:name=".receiver.SmsReceiver"
android:enabled="false"
+ android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter android:priority="2147483647">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
@@ -357,6 +358,7 @@
<!-- Registered for a priority just ahead of inbox Messaging apps (2) -->
<receiver android:name=".receiver.AbortMmsWapPushReceiver"
android:enabled="false"
+ android:exported="true"
android:permission="android.permission.BROADCAST_WAP_PUSH">
<intent-filter android:priority="3">
<action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
@@ -365,6 +367,7 @@
</receiver>
<receiver android:name=".receiver.AbortSmsReceiver"
android:enabled="false"
+ android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter android:priority="3">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
@@ -373,6 +376,7 @@
<!-- Intents for KLP+ Delivery -->
<receiver android:name=".receiver.MmsWapPushDeliverReceiver"
+ android:exported="true"
android:permission="android.permission.BROADCAST_WAP_PUSH">
<intent-filter>
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
@@ -380,6 +384,7 @@
</intent-filter>
</receiver>
<receiver android:name=".receiver.SmsDeliverReceiver"
+ android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_DELIVER" />
@@ -413,6 +418,8 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
+ <data android:scheme="mms" />
+ <data android:scheme="mmsto" />
</intent-filter>
</service>
@@ -441,7 +448,8 @@
android:theme="@style/BugleTheme"
android:parentActivityName="com.android.messaging.ui.appsettings.ApnSettingsActivity"/>
- <receiver android:name=".receiver.StorageStatusReceiver">
+ <receiver android:name=".receiver.StorageStatusReceiver"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.DEVICE_STORAGE_LOW" />
</intent-filter>
@@ -450,11 +458,12 @@
</intent-filter>
</receiver>
- <receiver android:name=".receiver.BootAndPackageReplacedReceiver">
+ <receiver android:name=".receiver.BootAndPackageReplacedReceiver"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
- </intent-filter>
+ </intent-filter>
</receiver>
<!-- Broadcast receiver that will be notified to reset notifications -->
@@ -472,7 +481,8 @@
</intent-filter>
</receiver>
- <receiver android:name=".receiver.DefaultSmsSubscriptionChangeReceiver">
+ <receiver android:name=".receiver.DefaultSmsSubscriptionChangeReceiver"
+ android:exported="true">
<intent-filter>
<action android:name="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED"/>
</intent-filter>
@@ -480,6 +490,7 @@
<!-- Widget that displays the conversation list -->
<receiver android:name=".widget.BugleWidgetProvider"
+ android:exported="true"
android:label="@string/widget_conversation_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -490,6 +501,7 @@
<!-- Widget that displays the messages of a single conversation -->
<receiver android:name=".widget.WidgetConversationProvider"
+ android:exported="true"
android:label="@string/widget_conversation_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -508,6 +520,7 @@
<activity android:name=".ui.WidgetPickConversationActivity"
android:theme="@style/BugleTheme"
+ android:exported="true"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
diff --git a/jni/Android.bp b/jni/Android.bp
new file mode 100644
index 0000000..22840ea
--- /dev/null
+++ b/jni/Android.bp
@@ -0,0 +1,40 @@
+// 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 {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_shared {
+ name: "libgiftranscode",
+ static_libs: ["libgif"],
+ // Link to Android logging (liblog.so) and dynamic linker (libdl.so) libraries
+ shared_libs: [
+ "liblog",
+ "libdl",
+ ],
+ // for including the jni.h file
+ header_libs: ["jni_headers"],
+ include_dirs: ["external/giflib"],
+ srcs: ["GifTranscoder.cpp"],
+ cflags: [
+ "-Wno-unused-parameter",
+ "-Wno-switch",
+ ],
+ sdk_version: "19",
+ // LLVM libc++
+ stl: "c++_static",
+ product_specific: true,
+}
diff --git a/jni/Android.mk b/jni/Android.mk
deleted file mode 100644
index 6b349a8..0000000
--- a/jni/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_LIBRARIES += libgif
-
-# Link to Android logging (liblog.so) and dynamic linker (libdl.so) libraries
-LOCAL_LDFLAGS := -llog -ldl
-
-LOCAL_C_INCLUDES := \
- external/giflib
-
-LOCAL_MODULE := libgiftranscode
-LOCAL_SRC_FILES := GifTranscoder.cpp
-
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter -Wno-switch
-
-LOCAL_SDK_VERSION := 19
-LOCAL_NDK_STL_VARIANT := c++_static # LLVM libc++
-
-LOCAL_PRODUCT_MODULE := true
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/jni/GifTranscoder.cpp b/jni/GifTranscoder.cpp
index 9169f1b..324d315 100644
--- a/jni/GifTranscoder.cpp
+++ b/jni/GifTranscoder.cpp
@@ -173,7 +173,7 @@
return false;
}
- // Sanity-check the current image position.
+ // Check the current image position.
if (gifIn->Image.Left < 0 ||
gifIn->Image.Top < 0 ||
gifIn->Image.Left + gifIn->Image.Width > gifIn->SWidth ||
diff --git a/proguard-test.flags b/proguard-test.flags
index 353970a..50f69a2 100755
--- a/proguard-test.flags
+++ b/proguard-test.flags
@@ -18,8 +18,6 @@
-dontobfuscate
-dontoptimize
-
-
# FLAG(dnotario): Until we rationalize how to handle tests (extensive unit tests will want similar
# settings to these, but maybe we want to require VisibleForTesting attribute), just expose all
# non-private methods. This means we cannot run tests on user builds for the moment.
@@ -35,7 +33,7 @@
!private *;
}
--keep class com.android.messaging.ui.contact.* { *; }
+-keep class com.android.messaging.ui.*.* { *; }
# Keep the classes needed by emma
-keep class com.vladium.** { *; }
diff --git a/proguard.flags b/proguard.flags
index cb04900..c5bbb42 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -21,7 +21,9 @@
-keepclassmembers class * {
@com.google.common.annotations.VisibleForTesting *;
}
--keep class com.android.messaging.ui.contact.* { *; }
+-keep class com.android.messaging.* { *; }
+-keep class com.android.messaging.*.* { *; }
+-keep class com.android.messaging.*.*.* { *; }
# Keep methods that have the @VisibleForAnimation annotation
-keep @interface com.android.messaging.annotation.VisibleForAnimation
@@ -39,10 +41,13 @@
-keep public class * extends androidx.fragment.app.Fragment
-keep public class com.android.vcard.* { *; }
+-keep class androidx.collection.* { *; }
-keep class androidx.core.* { *; }
-keep class androidx.core.*.* { *; }
-keep class androidx.appcompat.* { *; }
-keep class androidx.appcompat.*.* { *; }
+-keep class androidx.recyclerview.widget.* { *; }
+-keep class androidx.viewpager.widget.* { *; }
# Keep rastermill classes that need to be accessed from native code (JNI)
-keep class android.support.rastermill.** { *; }
diff --git a/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png b/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png
deleted file mode 100644
index e5f8942..0000000
--- a/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png b/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png
deleted file mode 100644
index 11ef458..0000000
--- a/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png b/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png
deleted file mode 100644
index eb948c1..0000000
--- a/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png b/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png
deleted file mode 100644
index b90188e..0000000
--- a/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/cab_bg.9.png b/res/drawable-hdpi/cab_bg.9.png
deleted file mode 100644
index 1adfbc6..0000000
--- a/res/drawable-hdpi/cab_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_add_gray.png b/res/drawable-hdpi/ic_add_gray.png
deleted file mode 100644
index dbbd78d..0000000
--- a/res/drawable-hdpi/ic_add_gray.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_archive_small_light.png b/res/drawable-hdpi/ic_archive_small_light.png
deleted file mode 100644
index 783126c..0000000
--- a/res/drawable-hdpi/ic_archive_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_archive_undo_small_light.png b/res/drawable-hdpi/ic_archive_undo_small_light.png
deleted file mode 100644
index 2c91e44..0000000
--- a/res/drawable-hdpi/ic_archive_undo_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_arrow_back_dark.png b/res/drawable-hdpi/ic_arrow_back_dark.png
deleted file mode 100644
index 4e5dc89..0000000
--- a/res/drawable-hdpi/ic_arrow_back_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_checkmark_small_blue.png b/res/drawable-hdpi/ic_checkmark_small_blue.png
deleted file mode 100644
index d84fd20..0000000
--- a/res/drawable-hdpi/ic_checkmark_small_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_color_lens.png b/res/drawable-hdpi/ic_color_lens.png
deleted file mode 100644
index 69a4287..0000000
--- a/res/drawable-hdpi/ic_color_lens.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_crying_hero.png b/res/drawable-hdpi/ic_crying_hero.png
deleted file mode 100644
index 804b4e6..0000000
--- a/res/drawable-hdpi/ic_crying_hero.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_delete_small_light.png b/res/drawable-hdpi/ic_delete_small_light.png
deleted file mode 100644
index d2ed6d3..0000000
--- a/res/drawable-hdpi/ic_delete_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_dnd_on_light.png b/res/drawable-hdpi/ic_dnd_on_light.png
deleted file mode 100644
index cadf5b5..0000000
--- a/res/drawable-hdpi/ic_dnd_on_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_history_light.png b/res/drawable-hdpi/ic_history_light.png
deleted file mode 100644
index 4b512fe..0000000
--- a/res/drawable-hdpi/ic_history_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_ime_dark.png b/res/drawable-hdpi/ic_ime_dark.png
deleted file mode 100644
index a539d28..0000000
--- a/res/drawable-hdpi/ic_ime_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_keyboard_arrow_left_light.png b/res/drawable-hdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 50f280e..0000000
--- a/res/drawable-hdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_keyboard_arrow_right_light.png b/res/drawable-hdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 3e31c08..0000000
--- a/res/drawable-hdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_mp_camera_large_light.png b/res/drawable-hdpi/ic_mp_camera_large_light.png
deleted file mode 100644
index 721b760..0000000
--- a/res/drawable-hdpi/ic_mp_camera_large_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_music.png b/res/drawable-hdpi/ic_music.png
new file mode 100644
index 0000000..033b402
--- /dev/null
+++ b/res/drawable-hdpi/ic_music.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_open_in_new.png b/res/drawable-hdpi/ic_open_in_new.png
deleted file mode 100644
index 06e5f71..0000000
--- a/res/drawable-hdpi/ic_open_in_new.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_person_add_blue.png b/res/drawable-hdpi/ic_person_add_blue.png
deleted file mode 100644
index b7b85bd..0000000
--- a/res/drawable-hdpi/ic_person_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_photo_library_light.png b/res/drawable-hdpi/ic_photo_library_light.png
index 1f71a62..55b956a 100644
--- a/res/drawable-hdpi/ic_photo_library_light.png
+++ b/res/drawable-hdpi/ic_photo_library_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_search_light.png b/res/drawable-hdpi/ic_search_light.png
deleted file mode 100644
index c35418e..0000000
--- a/res/drawable-hdpi/ic_search_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_sms_failed_light.png b/res/drawable-hdpi/ic_sms_failed_light.png
deleted file mode 100644
index 4973427..0000000
--- a/res/drawable-hdpi/ic_sms_failed_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_tooltip_arrow.png b/res/drawable-hdpi/ic_tooltip_arrow.png
deleted file mode 100644
index 870d502..0000000
--- a/res/drawable-hdpi/ic_tooltip_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/sym_keyboard_delete_holo.png b/res/drawable-hdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index 9e74a64..0000000
--- a/res/drawable-hdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_left_light.png b/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 3e31c08..0000000
--- a/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_right_light.png b/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 50f280e..0000000
--- a/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-hdpi/sym_keyboard_delete_holo_dark.png b/res/drawable-ldrtl-hdpi/sym_keyboard_delete_holo_dark.png
deleted file mode 100644
index f0e1b08..0000000
--- a/res/drawable-ldrtl-hdpi/sym_keyboard_delete_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_left_light.png b/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 9bd625d..0000000
--- a/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_right_light.png b/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 6004f37..0000000
--- a/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-mdpi/sym_keyboard_delete_holo_dark.png b/res/drawable-ldrtl-mdpi/sym_keyboard_delete_holo_dark.png
deleted file mode 100644
index 346e754..0000000
--- a/res/drawable-ldrtl-mdpi/sym_keyboard_delete_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 51a1903..0000000
--- a/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 464a0bc..0000000
--- a/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xhdpi/sym_keyboard_delete_holo_dark.png b/res/drawable-ldrtl-xhdpi/sym_keyboard_delete_holo_dark.png
deleted file mode 100644
index a662fc2..0000000
--- a/res/drawable-ldrtl-xhdpi/sym_keyboard_delete_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 62ca382..0000000
--- a/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 09536ed..0000000
--- a/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxhdpi/sym_keyboard_delete_holo_dark.png b/res/drawable-ldrtl-xxhdpi/sym_keyboard_delete_holo_dark.png
deleted file mode 100644
index 273efd6..0000000
--- a/res/drawable-ldrtl-xxhdpi/sym_keyboard_delete_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 85d005b..0000000
--- a/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 3737fec..0000000
--- a/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxxhdpi/sym_keyboard_delete_holo_dark.png b/res/drawable-ldrtl-xxxhdpi/sym_keyboard_delete_holo_dark.png
deleted file mode 100644
index 9203260..0000000
--- a/res/drawable-ldrtl-xxxhdpi/sym_keyboard_delete_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png b/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png
deleted file mode 100644
index 577ab08..0000000
--- a/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png b/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png
deleted file mode 100644
index d8525e4..0000000
--- a/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png b/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png
deleted file mode 100644
index 1b029e1..0000000
--- a/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png b/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png
deleted file mode 100644
index 4fa69f8..0000000
--- a/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/cab_bg.9.png b/res/drawable-mdpi/cab_bg.9.png
deleted file mode 100644
index f43a85b..0000000
--- a/res/drawable-mdpi/cab_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_add_gray.png b/res/drawable-mdpi/ic_add_gray.png
deleted file mode 100644
index 806b6e1..0000000
--- a/res/drawable-mdpi/ic_add_gray.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_archive_small_light.png b/res/drawable-mdpi/ic_archive_small_light.png
deleted file mode 100644
index fa3ffa9..0000000
--- a/res/drawable-mdpi/ic_archive_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_archive_undo_small_light.png b/res/drawable-mdpi/ic_archive_undo_small_light.png
deleted file mode 100644
index 08b2332..0000000
--- a/res/drawable-mdpi/ic_archive_undo_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_arrow_back_dark.png b/res/drawable-mdpi/ic_arrow_back_dark.png
deleted file mode 100644
index 75ae1b1..0000000
--- a/res/drawable-mdpi/ic_arrow_back_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_checkmark_small_blue.png b/res/drawable-mdpi/ic_checkmark_small_blue.png
deleted file mode 100644
index df6ea14..0000000
--- a/res/drawable-mdpi/ic_checkmark_small_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_color_lens.png b/res/drawable-mdpi/ic_color_lens.png
deleted file mode 100644
index b33c8b6..0000000
--- a/res/drawable-mdpi/ic_color_lens.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_crying_hero.png b/res/drawable-mdpi/ic_crying_hero.png
deleted file mode 100644
index d2f5eef..0000000
--- a/res/drawable-mdpi/ic_crying_hero.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_delete_small_light.png b/res/drawable-mdpi/ic_delete_small_light.png
deleted file mode 100644
index 641adc4..0000000
--- a/res/drawable-mdpi/ic_delete_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_dnd_on_light.png b/res/drawable-mdpi/ic_dnd_on_light.png
deleted file mode 100644
index 4936c2f..0000000
--- a/res/drawable-mdpi/ic_dnd_on_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_history_light.png b/res/drawable-mdpi/ic_history_light.png
deleted file mode 100644
index 234b2ce..0000000
--- a/res/drawable-mdpi/ic_history_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_ime_dark.png b/res/drawable-mdpi/ic_ime_dark.png
deleted file mode 100644
index 8c0a156..0000000
--- a/res/drawable-mdpi/ic_ime_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_keyboard_arrow_left_light.png b/res/drawable-mdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 6004f37..0000000
--- a/res/drawable-mdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_keyboard_arrow_right_light.png b/res/drawable-mdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 9bd625d..0000000
--- a/res/drawable-mdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_mp_camera_large_light.png b/res/drawable-mdpi/ic_mp_camera_large_light.png
deleted file mode 100644
index 3ec72f9..0000000
--- a/res/drawable-mdpi/ic_mp_camera_large_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_music.png b/res/drawable-mdpi/ic_music.png
new file mode 100644
index 0000000..106f912
--- /dev/null
+++ b/res/drawable-mdpi/ic_music.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_open_in_new.png b/res/drawable-mdpi/ic_open_in_new.png
deleted file mode 100644
index 44ef9d5..0000000
--- a/res/drawable-mdpi/ic_open_in_new.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_person_add_blue.png b/res/drawable-mdpi/ic_person_add_blue.png
deleted file mode 100644
index 0f63871..0000000
--- a/res/drawable-mdpi/ic_person_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_photo_library_light.png b/res/drawable-mdpi/ic_photo_library_light.png
index 322d486..26c58f3 100644
--- a/res/drawable-mdpi/ic_photo_library_light.png
+++ b/res/drawable-mdpi/ic_photo_library_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_search_light.png b/res/drawable-mdpi/ic_search_light.png
deleted file mode 100644
index b7e1de0..0000000
--- a/res/drawable-mdpi/ic_search_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_sms_failed_light.png b/res/drawable-mdpi/ic_sms_failed_light.png
deleted file mode 100644
index 9914e3f..0000000
--- a/res/drawable-mdpi/ic_sms_failed_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_tooltip_arrow.png b/res/drawable-mdpi/ic_tooltip_arrow.png
deleted file mode 100644
index 51824f5..0000000
--- a/res/drawable-mdpi/ic_tooltip_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/sym_keyboard_delete_holo.png b/res/drawable-mdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index 7b3103b..0000000
--- a/res/drawable-mdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png b/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png
deleted file mode 100644
index 96eb10e..0000000
--- a/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png
deleted file mode 100644
index 10986ee..0000000
--- a/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png b/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png
deleted file mode 100644
index 12f9a82..0000000
--- a/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png b/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png
deleted file mode 100644
index e7590b0..0000000
--- a/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/cab_bg.9.png b/res/drawable-xhdpi/cab_bg.9.png
deleted file mode 100644
index ba8c446..0000000
--- a/res/drawable-xhdpi/cab_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_add_gray.png b/res/drawable-xhdpi/ic_add_gray.png
deleted file mode 100644
index 8ce295d..0000000
--- a/res/drawable-xhdpi/ic_add_gray.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_archive_small_light.png b/res/drawable-xhdpi/ic_archive_small_light.png
deleted file mode 100644
index b3e628f..0000000
--- a/res/drawable-xhdpi/ic_archive_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_archive_undo_small_light.png b/res/drawable-xhdpi/ic_archive_undo_small_light.png
deleted file mode 100644
index b7038d8..0000000
--- a/res/drawable-xhdpi/ic_archive_undo_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_arrow_back_dark.png b/res/drawable-xhdpi/ic_arrow_back_dark.png
deleted file mode 100644
index c40b832..0000000
--- a/res/drawable-xhdpi/ic_arrow_back_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_checkmark_small_blue.png b/res/drawable-xhdpi/ic_checkmark_small_blue.png
deleted file mode 100644
index d6354df..0000000
--- a/res/drawable-xhdpi/ic_checkmark_small_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_color_lens.png b/res/drawable-xhdpi/ic_color_lens.png
deleted file mode 100644
index 8887429..0000000
--- a/res/drawable-xhdpi/ic_color_lens.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_crying_hero.png b/res/drawable-xhdpi/ic_crying_hero.png
deleted file mode 100644
index ccc40ab..0000000
--- a/res/drawable-xhdpi/ic_crying_hero.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_delete_small_light.png b/res/drawable-xhdpi/ic_delete_small_light.png
deleted file mode 100644
index 7c45f70..0000000
--- a/res/drawable-xhdpi/ic_delete_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_dnd_on_light.png b/res/drawable-xhdpi/ic_dnd_on_light.png
deleted file mode 100644
index e0527f5..0000000
--- a/res/drawable-xhdpi/ic_dnd_on_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_history_light.png b/res/drawable-xhdpi/ic_history_light.png
deleted file mode 100644
index 23585a2..0000000
--- a/res/drawable-xhdpi/ic_history_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_ime_dark.png b/res/drawable-xhdpi/ic_ime_dark.png
deleted file mode 100644
index cd5c5c6..0000000
--- a/res/drawable-xhdpi/ic_ime_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-xhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 464a0bc..0000000
--- a/res/drawable-xhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-xhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 51a1903..0000000
--- a/res/drawable-xhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_mp_camera_large_light.png b/res/drawable-xhdpi/ic_mp_camera_large_light.png
deleted file mode 100644
index 5552d92..0000000
--- a/res/drawable-xhdpi/ic_mp_camera_large_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_music.png b/res/drawable-xhdpi/ic_music.png
new file mode 100644
index 0000000..1de4c3b
--- /dev/null
+++ b/res/drawable-xhdpi/ic_music.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_open_in_new.png b/res/drawable-xhdpi/ic_open_in_new.png
deleted file mode 100644
index 363a35d..0000000
--- a/res/drawable-xhdpi/ic_open_in_new.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_person_add_blue.png b/res/drawable-xhdpi/ic_person_add_blue.png
deleted file mode 100644
index 0cdaeee..0000000
--- a/res/drawable-xhdpi/ic_person_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_photo_library_light.png b/res/drawable-xhdpi/ic_photo_library_light.png
index 1f5a69c..627ff48 100644
--- a/res/drawable-xhdpi/ic_photo_library_light.png
+++ b/res/drawable-xhdpi/ic_photo_library_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_search_light.png b/res/drawable-xhdpi/ic_search_light.png
deleted file mode 100644
index b83d895..0000000
--- a/res/drawable-xhdpi/ic_search_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_sms_failed_light.png b/res/drawable-xhdpi/ic_sms_failed_light.png
deleted file mode 100644
index 90c899a..0000000
--- a/res/drawable-xhdpi/ic_sms_failed_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_tooltip_arrow.png b/res/drawable-xhdpi/ic_tooltip_arrow.png
deleted file mode 100644
index 4a59931..0000000
--- a/res/drawable-xhdpi/ic_tooltip_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/sym_keyboard_delete_holo.png b/res/drawable-xhdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index 16b58a0..0000000
--- a/res/drawable-xhdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected.9.png b/res/drawable-xhdpi/tab_selected.9.png
deleted file mode 100644
index 95e5f43..0000000
--- a/res/drawable-xhdpi/tab_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_focused_holo.9.png b/res/drawable-xhdpi/tab_selected_focused_holo.9.png
deleted file mode 100644
index f98b4d9..0000000
--- a/res/drawable-xhdpi/tab_selected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_pressed_focused_holo.9.png b/res/drawable-xhdpi/tab_selected_pressed_focused_holo.9.png
deleted file mode 100644
index e8f2cb6..0000000
--- a/res/drawable-xhdpi/tab_selected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_pressed_holo.9.png b/res/drawable-xhdpi/tab_selected_pressed_holo.9.png
deleted file mode 100644
index 1882927..0000000
--- a/res/drawable-xhdpi/tab_selected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected.9.png b/res/drawable-xhdpi/tab_unselected.9.png
deleted file mode 100644
index 8cede8d..0000000
--- a/res/drawable-xhdpi/tab_unselected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_focused_holo.9.png b/res/drawable-xhdpi/tab_unselected_focused_holo.9.png
deleted file mode 100644
index 28a7ec5..0000000
--- a/res/drawable-xhdpi/tab_unselected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_pressed_focused_holo.9.png b/res/drawable-xhdpi/tab_unselected_pressed_focused_holo.9.png
deleted file mode 100644
index b9962b1..0000000
--- a/res/drawable-xhdpi/tab_unselected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png b/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png
deleted file mode 100644
index dadc471..0000000
--- a/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png b/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png
deleted file mode 100644
index cc880c2..0000000
--- a/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
deleted file mode 100644
index 60ccf97..0000000
--- a/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png b/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png
deleted file mode 100644
index 0358907..0000000
--- a/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png b/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png
deleted file mode 100644
index adcddc9..0000000
--- a/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/cab_bg.9.png b/res/drawable-xxhdpi/cab_bg.9.png
deleted file mode 100644
index b75401e..0000000
--- a/res/drawable-xxhdpi/cab_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_add_gray.png b/res/drawable-xxhdpi/ic_add_gray.png
deleted file mode 100644
index 8273830..0000000
--- a/res/drawable-xxhdpi/ic_add_gray.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_archive_small_light.png b/res/drawable-xxhdpi/ic_archive_small_light.png
deleted file mode 100644
index 042ed27..0000000
--- a/res/drawable-xxhdpi/ic_archive_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_archive_undo_small_light.png b/res/drawable-xxhdpi/ic_archive_undo_small_light.png
deleted file mode 100644
index cd9d0d5..0000000
--- a/res/drawable-xxhdpi/ic_archive_undo_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_arrow_back_dark.png b/res/drawable-xxhdpi/ic_arrow_back_dark.png
deleted file mode 100644
index 1c71cb0..0000000
--- a/res/drawable-xxhdpi/ic_arrow_back_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_checkmark_small_blue.png b/res/drawable-xxhdpi/ic_checkmark_small_blue.png
deleted file mode 100644
index 154c66e..0000000
--- a/res/drawable-xxhdpi/ic_checkmark_small_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_color_lens.png b/res/drawable-xxhdpi/ic_color_lens.png
deleted file mode 100644
index 6fd56fd..0000000
--- a/res/drawable-xxhdpi/ic_color_lens.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_crying_hero.png b/res/drawable-xxhdpi/ic_crying_hero.png
deleted file mode 100644
index 46538b5..0000000
--- a/res/drawable-xxhdpi/ic_crying_hero.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_delete_small_light.png b/res/drawable-xxhdpi/ic_delete_small_light.png
deleted file mode 100644
index b739ac9..0000000
--- a/res/drawable-xxhdpi/ic_delete_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_dnd_on_light.png b/res/drawable-xxhdpi/ic_dnd_on_light.png
deleted file mode 100644
index 3ddaa13..0000000
--- a/res/drawable-xxhdpi/ic_dnd_on_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_history_light.png b/res/drawable-xxhdpi/ic_history_light.png
deleted file mode 100644
index bd430f8..0000000
--- a/res/drawable-xxhdpi/ic_history_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_ime_dark.png b/res/drawable-xxhdpi/ic_ime_dark.png
deleted file mode 100644
index a32bd46..0000000
--- a/res/drawable-xxhdpi/ic_ime_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-xxhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 09536ed..0000000
--- a/res/drawable-xxhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-xxhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 62ca382..0000000
--- a/res/drawable-xxhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_mp_camera_large_light.png b/res/drawable-xxhdpi/ic_mp_camera_large_light.png
deleted file mode 100644
index 37fa935..0000000
--- a/res/drawable-xxhdpi/ic_mp_camera_large_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_music.png b/res/drawable-xxhdpi/ic_music.png
new file mode 100644
index 0000000..f8016cc
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_music.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_open_in_new.png b/res/drawable-xxhdpi/ic_open_in_new.png
deleted file mode 100644
index 9109fa4..0000000
--- a/res/drawable-xxhdpi/ic_open_in_new.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_person_add_blue.png b/res/drawable-xxhdpi/ic_person_add_blue.png
deleted file mode 100644
index d9b6a77..0000000
--- a/res/drawable-xxhdpi/ic_person_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_photo_library_light.png b/res/drawable-xxhdpi/ic_photo_library_light.png
index f61bb58..cd72bdf 100644
--- a/res/drawable-xxhdpi/ic_photo_library_light.png
+++ b/res/drawable-xxhdpi/ic_photo_library_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_search_light.png b/res/drawable-xxhdpi/ic_search_light.png
deleted file mode 100644
index 97d77d6..0000000
--- a/res/drawable-xxhdpi/ic_search_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_sms_failed_light.png b/res/drawable-xxhdpi/ic_sms_failed_light.png
deleted file mode 100644
index 5da454b..0000000
--- a/res/drawable-xxhdpi/ic_sms_failed_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_tooltip_arrow.png b/res/drawable-xxhdpi/ic_tooltip_arrow.png
deleted file mode 100644
index 72f1c5b..0000000
--- a/res/drawable-xxhdpi/ic_tooltip_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/sym_keyboard_delete_holo.png b/res/drawable-xxhdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index f2adbec..0000000
--- a/res/drawable-xxhdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected.9.png b/res/drawable-xxhdpi/tab_selected.9.png
deleted file mode 100644
index e5efc58..0000000
--- a/res/drawable-xxhdpi/tab_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_focused_holo.9.png b/res/drawable-xxhdpi/tab_selected_focused_holo.9.png
deleted file mode 100644
index 8762b9e..0000000
--- a/res/drawable-xxhdpi/tab_selected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_pressed_focused_holo.9.png b/res/drawable-xxhdpi/tab_selected_pressed_focused_holo.9.png
deleted file mode 100644
index 8faa7ff..0000000
--- a/res/drawable-xxhdpi/tab_selected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_pressed_holo.9.png b/res/drawable-xxhdpi/tab_selected_pressed_holo.9.png
deleted file mode 100644
index c7a539b..0000000
--- a/res/drawable-xxhdpi/tab_selected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected.9.png b/res/drawable-xxhdpi/tab_unselected.9.png
deleted file mode 100644
index 3891886..0000000
--- a/res/drawable-xxhdpi/tab_unselected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_focused_holo.9.png b/res/drawable-xxhdpi/tab_unselected_focused_holo.9.png
deleted file mode 100644
index 462ab15..0000000
--- a/res/drawable-xxhdpi/tab_unselected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_pressed_focused_holo.9.png b/res/drawable-xxhdpi/tab_unselected_pressed_focused_holo.9.png
deleted file mode 100644
index 58ddbe8..0000000
--- a/res/drawable-xxhdpi/tab_unselected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_pressed_holo.9.png b/res/drawable-xxhdpi/tab_unselected_pressed_holo.9.png
deleted file mode 100644
index de61933..0000000
--- a/res/drawable-xxhdpi/tab_unselected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/btn_keyboard_key_dark_normal_holo.9.png b/res/drawable-xxxhdpi/btn_keyboard_key_dark_normal_holo.9.png
deleted file mode 100644
index 65cd55e..0000000
--- a/res/drawable-xxxhdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/res/drawable-xxxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
deleted file mode 100644
index b6cac56..0000000
--- a/res/drawable-xxxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/btn_keyboard_key_light_normal_holo.9.png b/res/drawable-xxxhdpi/btn_keyboard_key_light_normal_holo.9.png
deleted file mode 100644
index f017e03..0000000
--- a/res/drawable-xxxhdpi/btn_keyboard_key_light_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/btn_keyboard_key_light_pressed_holo.9.png b/res/drawable-xxxhdpi/btn_keyboard_key_light_pressed_holo.9.png
deleted file mode 100644
index 025e694..0000000
--- a/res/drawable-xxxhdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/cab_bg.9.png b/res/drawable-xxxhdpi/cab_bg.9.png
deleted file mode 100644
index 978cfad..0000000
--- a/res/drawable-xxxhdpi/cab_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_archive_small_light.png b/res/drawable-xxxhdpi/ic_archive_small_light.png
deleted file mode 100644
index 59ffef9..0000000
--- a/res/drawable-xxxhdpi/ic_archive_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_archive_undo_small_light.png b/res/drawable-xxxhdpi/ic_archive_undo_small_light.png
deleted file mode 100644
index 46fd7d8..0000000
--- a/res/drawable-xxxhdpi/ic_archive_undo_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_arrow_back_dark.png b/res/drawable-xxxhdpi/ic_arrow_back_dark.png
deleted file mode 100644
index e7a9c81..0000000
--- a/res/drawable-xxxhdpi/ic_arrow_back_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_checkmark_small_blue.png b/res/drawable-xxxhdpi/ic_checkmark_small_blue.png
deleted file mode 100644
index 855cfc5..0000000
--- a/res/drawable-xxxhdpi/ic_checkmark_small_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_color_lens.png b/res/drawable-xxxhdpi/ic_color_lens.png
deleted file mode 100644
index 5c71071..0000000
--- a/res/drawable-xxxhdpi/ic_color_lens.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_crying_hero.png b/res/drawable-xxxhdpi/ic_crying_hero.png
deleted file mode 100644
index 0a428c9..0000000
--- a/res/drawable-xxxhdpi/ic_crying_hero.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_delete_small_light.png b/res/drawable-xxxhdpi/ic_delete_small_light.png
deleted file mode 100644
index d8a661f..0000000
--- a/res/drawable-xxxhdpi/ic_delete_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_dnd_on_light.png b/res/drawable-xxxhdpi/ic_dnd_on_light.png
deleted file mode 100644
index 7851985..0000000
--- a/res/drawable-xxxhdpi/ic_dnd_on_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_history_light.png b/res/drawable-xxxhdpi/ic_history_light.png
deleted file mode 100644
index 7ff42b7..0000000
--- a/res/drawable-xxxhdpi/ic_history_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_ime_dark.png b/res/drawable-xxxhdpi/ic_ime_dark.png
deleted file mode 100644
index 7c051df..0000000
--- a/res/drawable-xxxhdpi/ic_ime_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-xxxhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 3737fec..0000000
--- a/res/drawable-xxxhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-xxxhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 85d005b..0000000
--- a/res/drawable-xxxhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_mp_camera_large_light.png b/res/drawable-xxxhdpi/ic_mp_camera_large_light.png
deleted file mode 100644
index f915989..0000000
--- a/res/drawable-xxxhdpi/ic_mp_camera_large_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_music.png b/res/drawable-xxxhdpi/ic_music.png
new file mode 100644
index 0000000..11d67ae
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_music.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_open_in_new.png b/res/drawable-xxxhdpi/ic_open_in_new.png
deleted file mode 100644
index 6d6b59f..0000000
--- a/res/drawable-xxxhdpi/ic_open_in_new.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_person_add_blue.png b/res/drawable-xxxhdpi/ic_person_add_blue.png
deleted file mode 100644
index 13eb372..0000000
--- a/res/drawable-xxxhdpi/ic_person_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_photo_library_light.png b/res/drawable-xxxhdpi/ic_photo_library_light.png
index 8627f42..9fe62f4 100644
--- a/res/drawable-xxxhdpi/ic_photo_library_light.png
+++ b/res/drawable-xxxhdpi/ic_photo_library_light.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_search_light.png b/res/drawable-xxxhdpi/ic_search_light.png
deleted file mode 100644
index b38785a..0000000
--- a/res/drawable-xxxhdpi/ic_search_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_sms_failed_light.png b/res/drawable-xxxhdpi/ic_sms_failed_light.png
deleted file mode 100644
index 2daf2ae..0000000
--- a/res/drawable-xxxhdpi/ic_sms_failed_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_tooltip_arrow.png b/res/drawable-xxxhdpi/ic_tooltip_arrow.png
deleted file mode 100644
index 84b03ad..0000000
--- a/res/drawable-xxxhdpi/ic_tooltip_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/sym_keyboard_delete_holo.png b/res/drawable-xxxhdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index 09af002..0000000
--- a/res/drawable-xxxhdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_selected.9.png b/res/drawable-xxxhdpi/tab_selected.9.png
deleted file mode 100644
index 5c20d4d..0000000
--- a/res/drawable-xxxhdpi/tab_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_selected_focused_holo.9.png b/res/drawable-xxxhdpi/tab_selected_focused_holo.9.png
deleted file mode 100644
index 33be972..0000000
--- a/res/drawable-xxxhdpi/tab_selected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_selected_pressed_focused_holo.9.png b/res/drawable-xxxhdpi/tab_selected_pressed_focused_holo.9.png
deleted file mode 100644
index dc38a52..0000000
--- a/res/drawable-xxxhdpi/tab_selected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_selected_pressed_holo.9.png b/res/drawable-xxxhdpi/tab_selected_pressed_holo.9.png
deleted file mode 100644
index e4518a8..0000000
--- a/res/drawable-xxxhdpi/tab_selected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_unselected.9.png b/res/drawable-xxxhdpi/tab_unselected.9.png
deleted file mode 100644
index 9df735b..0000000
--- a/res/drawable-xxxhdpi/tab_unselected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_unselected_focused_holo.9.png b/res/drawable-xxxhdpi/tab_unselected_focused_holo.9.png
deleted file mode 100644
index f01cfaf..0000000
--- a/res/drawable-xxxhdpi/tab_unselected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_unselected_pressed_focused_holo.9.png b/res/drawable-xxxhdpi/tab_unselected_pressed_focused_holo.9.png
deleted file mode 100644
index 7d85b20..0000000
--- a/res/drawable-xxxhdpi/tab_unselected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_unselected_pressed_holo.9.png b/res/drawable-xxxhdpi/tab_unselected_pressed_holo.9.png
deleted file mode 100644
index 0ace170..0000000
--- a/res/drawable-xxxhdpi/tab_unselected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/exit_button_bg.xml b/res/drawable/exit_button_bg.xml
deleted file mode 100644
index 9fa0fb2..0000000
--- a/res/drawable/exit_button_bg.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@android:color/white">
-</ripple>
diff --git a/res/drawable/gallery_document_picker_item_background.xml b/res/drawable/gallery_document_picker_item_background.xml
deleted file mode 100644
index 4bbd7fe..0000000
--- a/res/drawable/gallery_document_picker_item_background.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <stroke android:color="@color/gallery_image_default_background" android:width="1dp" />
-</shape>
\ No newline at end of file
diff --git a/res/drawable/stat_notify_chat.png b/res/drawable/stat_notify_chat.png
deleted file mode 100644
index cc08e21..0000000
--- a/res/drawable/stat_notify_chat.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/tab_indicator.xml b/res/drawable/tab_indicator.xml
deleted file mode 100644
index 28ecda3..0000000
--- a/res/drawable/tab_indicator.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- Non focused states -->
- <item
- android:state_focused="false"
- android:state_selected="false"
- android:state_pressed="false"
- android:drawable="@drawable/tab_unselected" />
- <item
- android:state_focused="false"
- android:state_selected="true"
- android:state_pressed="false"
- android:drawable="@drawable/tab_selected" />
-
- <!-- Focused states -->
- <item
- android:state_focused="true"
- android:state_selected="false"
- android:state_pressed="false"
- android:drawable="@drawable/tab_unselected_focused_holo" />
- <item
- android:state_focused="true"
- android:state_selected="true"
- android:state_pressed="false"
- android:drawable="@drawable/tab_selected_focused_holo" />
-
- <!-- Pressed -->
- <!-- Non focused states -->
- <item
- android:state_focused="false"
- android:state_selected="false"
- android:state_pressed="true"
- android:drawable="@drawable/tab_unselected_pressed_holo" />
- <item
- android:state_focused="false"
- android:state_selected="true"
- android:state_pressed="true"
- android:drawable="@drawable/tab_selected_pressed_holo" />
-
- <!-- Focused states -->
- <item
- android:state_focused="true"
- android:state_selected="false"
- android:state_pressed="true"
- android:drawable="@drawable/tab_unselected_pressed_focused_holo" />
- <item
- android:state_focused="true"
- android:state_selected="true"
- android:state_pressed="true"
- android:drawable="@drawable/tab_selected_pressed_focused_holo" />
-</selector>
diff --git a/res/layout/apn_preference_layout.xml b/res/layout/apn_preference_layout.xml
index 8b864f6..12693d4 100644
--- a/res/layout/apn_preference_layout.xml
+++ b/res/layout/apn_preference_layout.xml
@@ -38,7 +38,7 @@
android:background="?android:attr/selectableItemBackground">
<TextView
- android:id="@+id/title"
+ android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true"
@@ -46,7 +46,7 @@
android:textAppearance="?android:attr/textAppearanceListItem" />
<TextView
- android:id="@+id/summary"
+ android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
diff --git a/res/layout/gallery_grid_item_view.xml b/res/layout/gallery_grid_item_view.xml
index fc857ca..f8f3920 100644
--- a/res/layout/gallery_grid_item_view.xml
+++ b/res/layout/gallery_grid_item_view.xml
@@ -85,6 +85,7 @@
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:focusableInTouchMode="true"
android:background="@drawable/gallery_image_background_selector" />
<CheckBox
diff --git a/res/layout/mediapicker_contact_chooser.xml b/res/layout/mediapicker_contact_chooser.xml
new file mode 100644
index 0000000..dcace4f
--- /dev/null
+++ b/res/layout/mediapicker_contact_chooser.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/white"
+ android:importantForAccessibility="no" >
+
+ <FrameLayout
+ android:id="@+id/mediapicker_enabled"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <TextView
+ style="@style/ContactPickerHintText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|center"
+ android:layout_marginTop="16dp"
+ android:text="@string/contact_picker_hint_text"
+ android:importantForAccessibility="no" />
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:scaleType="center"
+ android:src="@drawable/ic_person_light_large"
+ android:tint="@color/primary_color"
+ android:importantForAccessibility="no"
+ android:contentDescription="@null" />
+ </FrameLayout>
+
+ <!-- This view will hide all other views if the required permission is not granted -->
+ <TextView
+ android:id="@+id/missing_permission_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/enable_permission_procedure"
+ android:contentDescription="@string/enable_permission_procedure_description"
+ android:background="@android:color/white"
+ android:gravity="center"
+ android:visibility="gone" />
+
+</FrameLayout>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 035ed81..0ecd2c2 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -111,6 +111,8 @@
<color name="audio_attachment_timer_text_color">#323232</color>
<color name="audio_progress_bar_color">@color/primary_color</color>
+ <color name="contact_picker_hint_text_color">#40000000</color>
+
<color name="notification_sender_text">#9A9A9A</color>
<color name="notification_secondary_text">#FFFFFF</color>
<color name="notification_tertiary_text">#FFFFFF</color>
diff --git a/res/values/constants.xml b/res/values/constants.xml
index 8985fd1..e494e95 100644
--- a/res/values/constants.xml
+++ b/res/values/constants.xml
@@ -33,7 +33,7 @@
<string name="advanced_pref_key" translatable="false">advanced_prefs</string>
<!-- Subscription-specific settings. The values of these pref keys must be prefixed with
- "buglesub_" to allow for runtime sanity checks -->
+ "buglesub_" to allow for runtime checks -->
<string name="delivery_reports_pref_key" translatable="false">buglesub_delivery_reports</string>
<bool name="delivery_reports_pref_default" translatable="false">false</bool>
<string name="auto_retrieve_mms_pref_key" translatable="false">buglesub_auto_retrieve_mms</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d775c7e..0795ebc 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -100,6 +100,9 @@
<dimen name="audio_picker_text_size">16sp</dimen>
<dimen name="audio_attachment_text_size">14sp</dimen>
<dimen name="audio_progress_bar_height">6dp</dimen>
+
+ <dimen name="contact_picker_text_size">16sp</dimen>
+
<!-- Videos in the message list view should at least be this big in the smallest dimension -->
<dimen name="video_message_min_size">320dp</dimen>
<dimen name="attachment_rounded_corner_radius">3dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 521b1fa..e5c442d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -53,6 +53,7 @@
<string name="mediapicker_cameraChooserDescription">Capture pictures or video</string>
<string name="mediapicker_galleryChooserDescription">Choose images from this device</string>
<string name="mediapicker_audioChooserDescription">Record audio</string>
+ <string name="mediapicker_contactChooserDescription">Choose a contact from this device</string>
<string name="mediapicker_gallery_title">Choose media</string>
<string name="mediapicker_gallery_item_selected_content_description">The media is selected.</string>
<string name="mediapicker_gallery_item_unselected_content_description">The media is unselected.</string>
@@ -61,6 +62,9 @@
<string name="mediapicker_gallery_image_item_description">image <xliff:g id="date">%1$tB %1$te %1$tY %1$tl %1$tM %1$tp</xliff:g></string>
<string name="mediapicker_gallery_image_item_description_no_date">image</string>
<string name="mediapicker_audio_title">Record audio</string>
+ <string name="mediapicker_contact_title">Choose a contact</string>
+ <!-- Hint text on the contact picker -->
+ <string name="contact_picker_hint_text">Click to open contacts list on this device</string>
<string name="action_share">Share</string>
@@ -975,4 +979,7 @@
<string name="selected_sim_content_message"><xliff:g id="selected_sim">%s</xliff:g> selected</string>
<string name="work_directory_display_name">Work Profile contacts</string>
+
+ <!-- When making a call to emergency numbers, show this toast -->
+ <string name="disallow_emergency_call">Can\'t make a voice call to emergency services here.</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 46533af..6f9be47 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -217,6 +217,11 @@
<item name="android:paddingLeft">16dp</item>
</style>
+ <style name="ContactPickerHintText">
+ <item name="android:textSize">@dimen/contact_picker_text_size</item>
+ <item name="android:textColor">@color/contact_picker_hint_text_color</item>
+ </style>
+
<style name="VcardAttachmentSingleStyle">
<item name="android:paddingRight">@dimen/message_text_left_right_padding</item>
<item name="android:paddingLeft">@dimen/message_text_left_right_padding</item>
diff --git a/res/xml-mcc440-mnc11/mms_config.xml b/res/xml-mcc440-mnc11/mms_config.xml
new file mode 100644
index 0000000..2464a13
--- /dev/null
+++ b/res/xml-mcc440-mnc11/mms_config.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright 2013 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.
+-->
+
+<!-- Version History
+ version 1 - initial version.
+ version 2 - added recipientLimit.
+ version 3 - added min/max recycler values.
+ version 4 - added sms to mms text threshold.
+-->
+
+<!-- Rakuten Mobile J.P. mcc=440, mnc=11 -->
+
+<mms_config version="4">
+ <!-- Maximum message size in bytes for a MMS message -->
+ <int name="maxMessageSize">2097152</int>
+
+ <!-- Maximum height for an attached image -->
+ <int name="maxImageHeight">1944</int>
+
+ <!-- Maximum width for an attached image -->
+ <int name="maxImageWidth">2592</int>
+</mms_config>
\ No newline at end of file
diff --git a/res/xml/apns.xml b/res/xml/apns.xml
index 57e1163..20de298 100644
--- a/res/xml/apns.xml
+++ b/res/xml/apns.xml
@@ -836,10 +836,10 @@
<apn mcc="310" mnc="120" carrier="Sprint" apn="sprint" mmsc="http://mms.sprintpcs.com" mmsproxy="68.28.31.7" mmsport="80" type="default,supl,mms,ims,cbs,dun" bearer="8"></apn>
<apn mcc="310" mnc="120" carrier="Sprint CdmaNai" apn="CdmaNai" mmsc="http://mms.sprintpcs.com" mmsproxy="68.28.31.7" mmsport="80" type="mms" authtype="0" protocol="IPV4V6" bearer="6" roaming_protocol="IPV4V6" carrier_enabled="FALSE"></apn>
<apn mcc="310" mnc="120" carrier="Sprint CdmaNai" apn="CdmaNai" mmsc="http://mms.sprintpcs.com" mmsproxy="68.28.31.7" mmsport="80" type="mms" authtype="0" protocol="IPV4V6" bearer="6" roaming_protocol="IPV4V6" carrier_enabled="TRUE"></apn>
- <apn mcc="310" mnc="130" carrier="Carolina west" apn="VZWINTERNET" mmsc="http://mms.cwwmms.com/cww/mms.php" mmsproxy="204.117.091.161" mmsport="80" type="default,mms,dun,supl" mvno_match_data="carolinawest" mvno_type="spn" protocol="IPV4V6" bearer="13" roaming_protocol="IPV4V6"></apn>
- <apn mcc="310" mnc="130" carrier="Carolina west" apn="VZWINTERNET" mmsc="http://mms.cwwmms.com/cww/mms.php" mmsproxy="204.117.091.161" mmsport="80" type="default,mms,dun,supl" mvno_match_data="carolinawest" mvno_type="spn" protocol="IPV4V6" bearer="14" roaming_protocol="IPV4V6"></apn>
- <apn mcc="310" mnc="130" carrier="My Multi Media" apn="mms.c1.ama" mmsc="http://mms.iot1.com/amarillo/mms.php" type="mms" user="cell1mms" password="cell1"></apn>
- <apn mcc="310" mnc="130" carrier="carolinawest" apn="CdmaNai" mmsc="http://mms.cwwmms.com/cww/mms.php" mmsproxy="204.117.091.161" mmsport="80" type="mms" mvno_match_data="carolinawest" mvno_type="spn" protocol="IPV4V6" bearer="6" roaming_protocol="IPV4V6" carrier_enabled="false"></apn>
+ <apn mcc="310" mnc="130" carrier="Carolina West Wireless" apn="home.cww.com" mmsc="" mmsproxy="" mmsport="" type="default,supl,fota"></apn>
+ <apn mcc="310" mnc="130" carrier="Carolina West Wireless IMS" apn="ims" mmsc="" mmsproxy="" mmsport="" type="ims"></apn>
+ <apn mcc="310" mnc="130" carrier="Carolina West Wireless MMS" apn="mms" mmsc="http://mms.cwwmms.com/cww/mms.php" mmsproxy="" mmsport="" type="mms"></apn>
+ <apn mcc="310" mnc="130" carrier="Carolina West Wireless Tethering" apn="tethering.cww.com" mmsc="" mmsproxy="" mmsport="80" type="dun"></apn>
<apn mcc="310" mnc="150" carrier="internet" apn="ndo" mmsc="http://mmsc.aiowireless.net" mmsproxy="proxy.aiowireless.net" mmsport="80" type="default,mms,fota,hipri,supl"></apn>
<apn mcc="310" mnc="160" carrier="MetroPCS 160" apn="fast.metropcs.com" mmsc="http://metropcs.mmsmvno.com/mms/wapenc" type="default,supl,mms" mvno_match_data="6D" mvno_type="gid" protocol="IPV6" roaming_protocol="IP"></apn>
<apn mcc="310" mnc="160" carrier="T-Mobile US 160" apn="epc.tmobile.com" mmsc="http://mms.msg.eng.t-mobile.com/mms/wapenc"></apn>
diff --git a/src/android/support/v7/mms/MmsManager.java b/src/android/support/v7/mms/MmsManager.java
index 4a72a32..642a141 100644
--- a/src/android/support/v7/mms/MmsManager.java
+++ b/src/android/support/v7/mms/MmsManager.java
@@ -135,13 +135,13 @@
*/
public static void sendMultimediaMessage(int subId, Context context, Uri contentUri,
String locationUrl, PendingIntent sentIntent) {
- if (Utils.hasMmsApi() && !sForceLegacyMms) {
+ if (shouldUseLegacyMms()) {
+ MmsService.startRequest(context, new SendRequest(locationUrl, contentUri, sentIntent));
+ } else {
subId = Utils.getEffectiveSubscriptionId(subId);
final SmsManager smsManager = Utils.getSmsManager(subId);
smsManager.sendMultimediaMessage(context, contentUri, locationUrl,
getConfigOverrides(subId), sentIntent);
- } else {
- MmsService.startRequest(context, new SendRequest(locationUrl, contentUri, sentIntent));
}
}
@@ -157,18 +157,27 @@
*/
public static void downloadMultimediaMessage(int subId, Context context, String locationUrl,
Uri contentUri, PendingIntent downloadedIntent) {
- if (Utils.hasMmsApi() && !sForceLegacyMms) {
+ if (shouldUseLegacyMms()) {
+ MmsService.startRequest(context,
+ new DownloadRequest(locationUrl, contentUri, downloadedIntent));
+ } else {
subId = Utils.getEffectiveSubscriptionId(subId);
final SmsManager smsManager = Utils.getSmsManager(subId);
smsManager.downloadMultimediaMessage(context, locationUrl, contentUri,
getConfigOverrides(subId), downloadedIntent);
- } else {
- MmsService.startRequest(context,
- new DownloadRequest(locationUrl, contentUri, downloadedIntent));
}
}
/**
+ * Checks if we should use legacy APIs for MMS.
+ *
+ * @return true if forced to use legacy APIs or platform doesn't supports MMS APIs.
+ */
+ public static boolean shouldUseLegacyMms() {
+ return sForceLegacyMms || !Utils.hasMmsApi();
+ }
+
+ /**
* Get carrier configuration values overrides when platform MMS API is called.
* We only need to compute this if customized carrier config values loader or
* user agent info loader are set
diff --git a/src/com/android/messaging/datamodel/BugleDatabaseOperations.java b/src/com/android/messaging/datamodel/BugleDatabaseOperations.java
index 281a8dd..c4e5fb8 100644
--- a/src/com/android/messaging/datamodel/BugleDatabaseOperations.java
+++ b/src/com/android/messaging/datamodel/BugleDatabaseOperations.java
@@ -965,7 +965,7 @@
return participant;
}
- static int getSelfSubscriptionId(final DatabaseWrapper dbWrapper,
+ public static int getSelfSubscriptionId(final DatabaseWrapper dbWrapper,
final String selfParticipantId) {
final ParticipantData selfParticipant = BugleDatabaseOperations.getExistingParticipant(
dbWrapper, selfParticipantId);
diff --git a/src/com/android/messaging/datamodel/DataModel.java b/src/com/android/messaging/datamodel/DataModel.java
index 936b51c..b606c72 100644
--- a/src/com/android/messaging/datamodel/DataModel.java
+++ b/src/com/android/messaging/datamodel/DataModel.java
@@ -50,7 +50,6 @@
import com.android.messaging.datamodel.data.SubscriptionListData;
import com.android.messaging.datamodel.data.VCardContactItemData;
import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
-import com.android.messaging.util.ConnectivityUtil;
public abstract class DataModel {
private String mFocusedConversation;
@@ -152,7 +151,5 @@
public abstract void onApplicationCreated();
- public abstract ConnectivityUtil getConnectivityUtil();
-
public abstract SyncManager getSyncManager();
}
diff --git a/src/com/android/messaging/datamodel/DataModelImpl.java b/src/com/android/messaging/datamodel/DataModelImpl.java
index 6ab3f00..ddb17ff 100644
--- a/src/com/android/messaging/datamodel/DataModelImpl.java
+++ b/src/com/android/messaging/datamodel/DataModelImpl.java
@@ -57,21 +57,27 @@
import com.android.messaging.util.OsUtil;
import com.android.messaging.util.PhoneUtils;
+import java.util.concurrent.ConcurrentHashMap;
+
public class DataModelImpl extends DataModel {
private final Context mContext;
private final ActionService mActionService;
private final BackgroundWorker mDataModelWorker;
private final DatabaseHelper mDatabaseHelper;
- private final ConnectivityUtil mConnectivityUtil;
private final SyncManager mSyncManager;
+ // Cached ConnectivityUtil instance for Pre-N.
+ private static ConnectivityUtil sConnectivityUtilInstanceCachePreN = null;
+ // Cached ConnectivityUtil subId->instance for N and beyond
+ private static final ConcurrentHashMap<Integer, ConnectivityUtil>
+ sConnectivityUtilInstanceCacheN = new ConcurrentHashMap<>();
+
public DataModelImpl(final Context context) {
super();
mContext = context;
mActionService = new ActionService();
mDataModelWorker = new BackgroundWorker();
mDatabaseHelper = DatabaseHelper.getInstance(context);
- mConnectivityUtil = new ConnectivityUtil(context);
mSyncManager = new SyncManager();
}
@@ -185,11 +191,6 @@
}
@Override
- public ConnectivityUtil getConnectivityUtil() {
- return mConnectivityUtil;
- }
-
- @Override
public SyncManager getSyncManager() {
return mSyncManager;
}
@@ -213,12 +214,19 @@
@Override
public void onApplicationCreated() {
+ if (OsUtil.isAtLeastN()) {
+ createConnectivityUtilForEachActiveSubscription();
+ } else {
+ sConnectivityUtilInstanceCachePreN = new ConnectivityUtil(mContext);
+ }
+
FixupMessageStatusOnStartupAction.fixupMessageStatus();
ProcessPendingMessagesAction.processFirstPendingMessage();
SyncManager.immediateSync();
if (OsUtil.isAtLeastL_MR1()) {
- // Start listening for subscription change events for refreshing self participants.
+ // Start listening for subscription change events for refreshing any data associated
+ // with subscriptions.
PhoneUtils.getDefault().toLMr1().registerOnSubscriptionsChangedListener(
new SubscriptionManager.OnSubscriptionsChangedListener() {
@Override
@@ -229,8 +237,35 @@
// gracefully
MmsConfig.loadAsync();
ParticipantRefresh.refreshSelfParticipants();
+ if (OsUtil.isAtLeastN()) {
+ createConnectivityUtilForEachActiveSubscription();
+ }
}
});
}
}
+
+ private void createConnectivityUtilForEachActiveSubscription() {
+ PhoneUtils.forEachActiveSubscription(new PhoneUtils.SubscriptionRunnable() {
+ @Override
+ public void runForSubscription(int subId) {
+ // Create the ConnectivityUtil instance for given subId if absent.
+ if (subId <= ParticipantData.DEFAULT_SELF_SUB_ID) {
+ subId = PhoneUtils.getDefault().getDefaultSmsSubscriptionId();
+ }
+ if (!sConnectivityUtilInstanceCacheN.containsKey(subId)) {
+ sConnectivityUtilInstanceCacheN.put(
+ subId, new ConnectivityUtil(mContext, subId));
+ }
+ }
+ });
+ }
+
+ public static ConnectivityUtil getConnectivityUtil(final int subId) {
+ if (OsUtil.isAtLeastN()) {
+ return sConnectivityUtilInstanceCacheN.get(subId);
+ } else {
+ return sConnectivityUtilInstanceCachePreN;
+ }
+ }
}
diff --git a/src/com/android/messaging/datamodel/DatabaseHelper.java b/src/com/android/messaging/datamodel/DatabaseHelper.java
index 2025e2c..486973e 100644
--- a/src/com/android/messaging/datamodel/DatabaseHelper.java
+++ b/src/com/android/messaging/datamodel/DatabaseHelper.java
@@ -49,10 +49,13 @@
return Integer.parseInt(context.getResources().getString(R.string.database_version));
}
- /** Table containing names of all other tables and views */
- private static final String MASTER_TABLE = "sqlite_master";
+ /**
+ * Table containing names of all other tables and views.
+ * TODO(rtenneti): Fix the following special SQLLite table name when SQLLite changes.
+ */
+ private static final String PRIMARY_TABLE = "sqlite_master";
/** Column containing the name of the tables and views */
- private static final String[] MASTER_COLUMNS = new String[] { "name", };
+ private static final String[] PRIMARY_COLUMNS = new String[] { "name", };
// Table names
public static final String CONVERSATIONS_TABLE = "conversations";
@@ -603,7 +606,6 @@
*/
@VisibleForTesting
static DatabaseHelper getNewInstanceForTest(final Context context) {
- Assert.isEngBuild();
Assert.isTrue(BugleApplication.isRunningTests());
return new DatabaseHelper(context);
}
@@ -683,7 +685,7 @@
*/
private static void dropAllTables(final SQLiteDatabase db) {
final Cursor tableCursor =
- db.query(MASTER_TABLE, MASTER_COLUMNS, "type='table'", null, null, null, null);
+ db.query(PRIMARY_TABLE, PRIMARY_COLUMNS, "type='table'", null, null, null, null);
if (tableCursor != null) {
try {
final String dropPrefix = "DROP TABLE IF EXISTS ";
@@ -714,7 +716,7 @@
*/
private static void dropAllTriggers(final SQLiteDatabase db) {
final Cursor triggerCursor =
- db.query(MASTER_TABLE, MASTER_COLUMNS, "type='trigger'", null, null, null, null);
+ db.query(PRIMARY_TABLE, PRIMARY_COLUMNS, "type='trigger'", null, null, null, null);
if (triggerCursor != null) {
try {
final String dropPrefix = "DROP TRIGGER IF EXISTS ";
@@ -745,7 +747,7 @@
*/
public static void dropAllViews(final SQLiteDatabase db) {
final Cursor viewCursor =
- db.query(MASTER_TABLE, MASTER_COLUMNS, "type='view'", null, null, null, null);
+ db.query(PRIMARY_TABLE, PRIMARY_COLUMNS, "type='view'", null, null, null, null);
if (viewCursor != null) {
try {
while (viewCursor.moveToNext()) {
@@ -763,7 +765,7 @@
*/
private static void dropAllIndexes(final SQLiteDatabase db) {
final Cursor indexCursor =
- db.query(MASTER_TABLE, MASTER_COLUMNS, "type='index'", null, null, null, null);
+ db.query(PRIMARY_TABLE, PRIMARY_COLUMNS, "type='index'", null, null, null, null);
if (indexCursor != null) {
try {
final String dropPrefix = "DROP INDEX IF EXISTS ";
diff --git a/src/com/android/messaging/datamodel/MessageNotificationState.java b/src/com/android/messaging/datamodel/MessageNotificationState.java
index 1c66f89..6b858fa 100644
--- a/src/com/android/messaging/datamodel/MessageNotificationState.java
+++ b/src/com/android/messaging/datamodel/MessageNotificationState.java
@@ -100,7 +100,6 @@
protected CharSequence mContent = null;
protected Uri mAttachmentUri = null;
protected String mAttachmentType = null;
- protected boolean mTickerNoContent;
@Override
protected Uri getAttachmentUri() {
@@ -1080,8 +1079,10 @@
}
if (state != null && LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
LogUtil.v(TAG, "MessageNotificationState: Notification state created"
- + ", title = " + LogUtil.sanitizePII(state.mTitle)
- + ", content = " + LogUtil.sanitizePII(state.mContent.toString()));
+ + ", title = "
+ + (state.mTickerSender != null ? state.mTickerSender : state.mTitle)
+ + ", content = "
+ + (state.mTickerText != null ? state.mTickerText : state.mContent));
}
return state;
}
@@ -1121,8 +1122,9 @@
protected CharSequence getTicker() {
return BugleNotifications.buildColonSeparatedMessage(
mTickerSender != null ? mTickerSender : mTitle,
- mTickerText != null ? mTickerText : (mTickerNoContent ? null : mContent), null,
- null);
+ mTickerText != null ? mTickerText : mContent,
+ null,
+ null);
}
private static CharSequence convertHtmlAndStripUrls(final String s) {
diff --git a/src/com/android/messaging/datamodel/NoConfirmationSmsSendService.java b/src/com/android/messaging/datamodel/NoConfirmationSmsSendService.java
index b5d2185..bea4961 100644
--- a/src/com/android/messaging/datamodel/NoConfirmationSmsSendService.java
+++ b/src/com/android/messaging/datamodel/NoConfirmationSmsSendService.java
@@ -28,10 +28,10 @@
import com.android.messaging.datamodel.action.UpdateMessageNotificationAction;
import com.android.messaging.datamodel.data.MessageData;
import com.android.messaging.datamodel.data.ParticipantData;
-import com.android.messaging.sms.MmsUtils;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.ui.conversationlist.ConversationListActivity;
import com.android.messaging.util.LogUtil;
+import com.android.messaging.util.UriUtil;
/**
* Respond to a special intent and send an SMS message without the user's intervention, unless
@@ -83,8 +83,8 @@
final String subject = getText(intent, Intent.EXTRA_SUBJECT);
final int subId = extras.getInt(EXTRA_SUBSCRIPTION, ParticipantData.DEFAULT_SELF_SUB_ID);
- final Uri intentUri = intent.getData();
- final String recipients = intentUri != null ? MmsUtils.getSmsRecipients(intentUri) : null;
+ // Get a comma-separated list of recipients
+ final String recipients = UriUtil.parseRecipientsFromSmsMmsUri(intent.getData());
if (TextUtils.isEmpty(recipients) && TextUtils.isEmpty(conversationId)) {
if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
diff --git a/src/com/android/messaging/datamodel/action/ActionServiceImpl.java b/src/com/android/messaging/datamodel/action/ActionServiceImpl.java
index 6725dde..d78bdc0 100644
--- a/src/com/android/messaging/datamodel/action/ActionServiceImpl.java
+++ b/src/com/android/messaging/datamodel/action/ActionServiceImpl.java
@@ -28,7 +28,6 @@
import com.android.messaging.Factory;
import com.android.messaging.datamodel.DataModel;
-import com.android.messaging.util.ConnectivityUtil;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.LoggingTimer;
import com.google.common.annotations.VisibleForTesting;
@@ -134,7 +133,6 @@
protected static final String BUNDLE_ACTION = "bundle_action";
private BackgroundWorker mBackgroundWorker;
- private ConnectivityUtil mConnectivityUtil;
/**
* Allocate an intent with a specific opcode.
@@ -213,14 +211,11 @@
public void onCreate() {
super.onCreate();
mBackgroundWorker = DataModel.get().getBackgroundWorkerForActionService();
- mConnectivityUtil = DataModel.get().getConnectivityUtil();
- mConnectivityUtil.registerForSignalStrength();
}
@Override
public void onDestroy() {
super.onDestroy();
- mConnectivityUtil.unregisterForSignalStrength();
}
/**
diff --git a/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java b/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java
index e3d131d..45e2474 100644
--- a/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java
+++ b/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java
@@ -60,13 +60,12 @@
final ContentValues values = new ContentValues();
values.put(DatabaseHelper.MessageColumns.STATUS,
MessageData.BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED);
- downloadFailedCnt += db.update(DatabaseHelper.MESSAGES_TABLE, values,
+ downloadFailedCnt = db.update(DatabaseHelper.MESSAGES_TABLE, values,
DatabaseHelper.MessageColumns.STATUS + " IN (?, ?)",
new String[]{
Integer.toString(MessageData.BUGLE_STATUS_INCOMING_AUTO_DOWNLOADING),
Integer.toString(MessageData.BUGLE_STATUS_INCOMING_MANUAL_DOWNLOADING)
});
- values.clear();
values.clear();
values.put(DatabaseHelper.MessageColumns.STATUS,
diff --git a/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java b/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java
index 2567ca9..8bf6d5e 100644
--- a/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java
@@ -118,7 +118,6 @@
*/
@Override
protected Object executeAction() {
- LogUtil.i(TAG, "InsertNewMessageAction: inserting new message");
MessageData message = actionParameters.getParcelable(KEY_MESSAGE);
if (message == null) {
LogUtil.i(TAG, "InsertNewMessageAction: Creating MessageData with provided data");
@@ -151,6 +150,8 @@
return null;
}
final int subId = self.getSubId();
+ LogUtil.i(TAG, "InsertNewMessageAction: inserting new message for subId " + subId);
+ actionParameters.putInt(KEY_SUB_ID, subId);
// TODO: Work out whether to send with SMS or MMS (taking into account recipients)?
final boolean isSms = (message.getProtocol() == MessageData.PROTOCOL_SMS);
diff --git a/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java b/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java
index 07e2cfb..60d3e35 100644
--- a/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java
+++ b/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java
@@ -83,7 +83,7 @@
// Set when message downloaded by us (legacy)
private static final String KEY_STATUS = "status";
private static final String KEY_RAW_STATUS = "raw_status";
- private static final String KEY_MMS_URI = "mms_uri";
+ private static final String KEY_MMS_URI = "mms_uri";
// Used to send a deferred response in response to auto-download failure
private static final String KEY_SEND_DEFERRED_RESP_STATUS = "send_deferred_resp_status";
@@ -329,6 +329,8 @@
if (response == null) {
// No message download to process; doBackgroundWork sent a notify deferred response
Assert.isTrue(actionParameters.getBoolean(KEY_SEND_DEFERRED_RESP_STATUS));
+ ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(
+ true /* failed */, this);
return null;
}
@@ -343,7 +345,9 @@
final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
// If we were trying to auto-download but have failed need to send the deferred response
- if (autoDownload && message == null && status == MmsUtils.MMS_REQUEST_MANUAL_RETRY) {
+ final boolean needToSendDeferredResp =
+ autoDownload && (status == MmsUtils.MMS_REQUEST_MANUAL_RETRY);
+ if (needToSendDeferredResp) {
final String transactionId = actionParameters.getString(KEY_TRANSACTION_ID);
final String contentLocation = actionParameters.getString(KEY_CONTENT_LOCATION);
sendDeferredRespStatus(messageId, transactionId, contentLocation, subId);
@@ -373,7 +377,11 @@
}
final boolean failed = (messageUri == null);
- ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(failed, this);
+ // Scheduling pending messages. If auto downloading is failed and it needs to send the
+ // deferred response, Skip it here and it will be scheduled after sending the response.
+ if (!needToSendDeferredResp) {
+ ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(failed, this);
+ }
if (failed) {
BugleNotifications.update(false, BugleNotifications.UPDATE_ERRORS);
}
diff --git a/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java b/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java
index ecbec10..a897ce0 100644
--- a/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java
+++ b/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java
@@ -17,76 +17,83 @@
package com.android.messaging.datamodel.action;
import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
import android.database.Cursor;
-import android.net.ConnectivityManager;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
import com.android.messaging.Factory;
import com.android.messaging.datamodel.BugleDatabaseOperations;
import com.android.messaging.datamodel.DataModel;
+import com.android.messaging.datamodel.DataModelImpl;
import com.android.messaging.datamodel.DatabaseHelper;
import com.android.messaging.datamodel.DatabaseHelper.MessageColumns;
-import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns;
import com.android.messaging.datamodel.DatabaseWrapper;
import com.android.messaging.datamodel.MessagingContentProvider;
import com.android.messaging.datamodel.data.MessageData;
import com.android.messaging.datamodel.data.ParticipantData;
-import com.android.messaging.sms.MmsUtils;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.BuglePrefs;
import com.android.messaging.util.BuglePrefsKeys;
+import com.android.messaging.util.ConnectivityUtil;
import com.android.messaging.util.ConnectivityUtil.ConnectivityListener;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.OsUtil;
import com.android.messaging.util.PhoneUtils;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
/**
* Action used to lookup any messages in the pending send/download state and either fail them or
- * retry their action. This action only initiates one retry at a time - further retries should be
- * triggered by successful sending of a message, network status change or exponential backoff timer.
+ * retry their action based on subscriptions. This action only initiates one retry at a time for
+ * both sending/downloading. Further retries should be triggered by successful sending/downloading
+ * of a message, network status change or exponential backoff timer.
*/
public class ProcessPendingMessagesAction extends Action implements Parcelable {
private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
- private static final int PENDING_INTENT_REQUEST_CODE = 101;
+ // PENDING_INTENT_BASE_REQUEST_CODE + subId(-1 for pre-L_MR1) is used per subscription uniquely.
+ private static final int PENDING_INTENT_BASE_REQUEST_CODE = 103;
+
+ private static final String KEY_SUB_ID = "sub_id";
public static void processFirstPendingMessage() {
- // Clear any pending alarms or connectivity events
- unregister();
- // Clear retry count
- setRetry(0);
-
- // Start action
- final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
- action.start();
+ PhoneUtils.forEachActiveSubscription(new PhoneUtils.SubscriptionRunnable() {
+ @Override
+ public void runForSubscription(final int subId) {
+ // Clear any pending alarms or connectivity events
+ unregister(subId);
+ // Clear retry count
+ setRetry(0, subId);
+ // Start action
+ final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
+ action.actionParameters.putInt(KEY_SUB_ID, subId);
+ action.start();
+ }
+ });
}
public static void scheduleProcessPendingMessagesAction(final boolean failed,
final Action processingAction) {
+ final int subId = processingAction.actionParameters
+ .getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
LogUtil.i(TAG, "ProcessPendingMessagesAction: Scheduling pending messages"
- + (failed ? "(message failed)" : ""));
+ + (failed ? "(message failed)" : "") + " for subId " + subId);
// Can safely clear any pending alarms or connectivity events as either an action
// is currently running or we will run now or register if pending actions possible.
- unregister();
+ unregister(subId);
final boolean isDefaultSmsApp = PhoneUtils.getDefault().isDefaultSmsApp();
boolean scheduleAlarm = false;
// If message succeeded and if Bugle is default SMS app just carry on with next message
if (!failed && isDefaultSmsApp) {
// Clear retry attempt count as something just succeeded
- setRetry(0);
+ setRetry(0, subId);
- // Lookup and queue next message for immediate processing by background worker
- // iff there are no pending messages this will do nothing and return true.
+ // Lookup and queue next message for each sending/downloading for immediate processing
+ // by background worker. If there are no pending messages, this will do nothing and
+ // return true.
final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
if (action.queueActions(processingAction)) {
if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
@@ -103,66 +110,53 @@
scheduleAlarm = true;
LogUtil.w(TAG, "ProcessPendingMessagesAction: Action failed to queue; retrying");
}
- if (getHavePendingMessages() || scheduleAlarm) {
+ if (getHavePendingMessages(subId) || scheduleAlarm) {
// Still have a pending message that needs to be queued for processing
final ConnectivityListener listener = new ConnectivityListener() {
@Override
- public void onConnectivityStateChanged(final Context context, final Intent intent) {
- final int networkType =
- MmsUtils.getConnectivityEventNetworkType(context, intent);
- if (networkType != ConnectivityManager.TYPE_MOBILE) {
- return;
- }
- final boolean isConnected = !intent.getBooleanExtra(
- ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
- // TODO: Should we check in more detail?
- if (isConnected) {
- onConnected();
- }
- }
-
- @Override
- public void onPhoneStateChanged(final Context context, final int serviceState) {
+ public void onPhoneStateChanged(final int serviceState) {
if (serviceState == ServiceState.STATE_IN_SERVICE) {
- onConnected();
+ LogUtil.i(TAG, "ProcessPendingMessagesAction: Now connected for subId "
+ + subId + ", starting action");
+
+ // Clear any pending alarms or connectivity events but leave attempt count
+ // alone
+ unregister(subId);
+
+ // Start action
+ final ProcessPendingMessagesAction action =
+ new ProcessPendingMessagesAction();
+ action.actionParameters.putInt(KEY_SUB_ID, subId);
+ action.start();
}
}
-
- private void onConnected() {
- LogUtil.i(TAG, "ProcessPendingMessagesAction: Now connected; starting action");
-
- // Clear any pending alarms or connectivity events but leave attempt count alone
- unregister();
-
- // Start action
- final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
- action.start();
- }
};
// Read and increment attempt number from shared prefs
- final int retryAttempt = getNextRetry();
- register(listener, retryAttempt);
+ final int retryAttempt = getNextRetry(subId);
+ register(listener, retryAttempt, subId);
} else {
// No more pending messages (presumably the message that failed has expired) or it
// may be possible that a send and a download are already in process.
// Clear retry attempt count.
// TODO Might be premature if send and download in process...
- // but worst case means we try to send a bit more often.
- setRetry(0);
-
- if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
- LogUtil.v(TAG, "ProcessPendingMessagesAction: No more pending messages");
- }
+ // but worst case means we try to send a bit more often.
+ setRetry(0, subId);
+ LogUtil.i(TAG, "ProcessPendingMessagesAction: No more pending messages");
}
}
- private static void register(final ConnectivityListener listener, final int retryAttempt) {
+ private static void register(final ConnectivityListener listener, final int retryAttempt,
+ int subId) {
int retryNumber = retryAttempt;
// Register to be notified about connectivity changes
- DataModel.get().getConnectivityUtil().register(listener);
+ ConnectivityUtil connectivityUtil = DataModelImpl.getConnectivityUtil(subId);
+ if (connectivityUtil != null) {
+ connectivityUtil.register(listener);
+ }
final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
+ action.actionParameters.putInt(KEY_SUB_ID, subId);
final long initialBackoffMs = BugleGservices.get().getLong(
BugleGservicesKeys.INITIAL_MESSAGE_RESEND_DELAY_MS,
BugleGservicesKeys.INITIAL_MESSAGE_RESEND_DELAY_MS_DEFAULT);
@@ -179,31 +173,34 @@
while (retryNumber > 0 && nextDelayMs < maxDelayMs);
LogUtil.i(TAG, "ProcessPendingMessagesAction: Registering for retry #" + retryAttempt
- + " in " + delayMs + " ms");
+ + " in " + delayMs + " ms for subId " + subId);
- action.schedule(PENDING_INTENT_REQUEST_CODE, delayMs);
+ action.schedule(PENDING_INTENT_BASE_REQUEST_CODE + subId, delayMs);
}
- private static void unregister() {
+ private static void unregister(final int subId) {
// Clear any pending alarms or connectivity events
- DataModel.get().getConnectivityUtil().unregister();
+ ConnectivityUtil connectivityUtil = DataModelImpl.getConnectivityUtil(subId);
+ if (connectivityUtil != null) {
+ connectivityUtil.unregister();
+ }
final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
- action.schedule(PENDING_INTENT_REQUEST_CODE, Long.MAX_VALUE);
+ action.schedule(PENDING_INTENT_BASE_REQUEST_CODE + subId, Long.MAX_VALUE);
if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
LogUtil.v(TAG, "ProcessPendingMessagesAction: Unregistering for connectivity changed "
- + "events and clearing scheduled alarm");
+ + "events and clearing scheduled alarm for subId " + subId);
}
}
- private static void setRetry(final int retryAttempt) {
- final BuglePrefs prefs = Factory.get().getApplicationPrefs();
+ private static void setRetry(final int retryAttempt, int subId) {
+ final BuglePrefs prefs = Factory.get().getSubscriptionPrefs(subId);
prefs.putInt(BuglePrefsKeys.PROCESS_PENDING_MESSAGES_RETRY_COUNT, retryAttempt);
}
- private static int getNextRetry() {
- final BuglePrefs prefs = Factory.get().getApplicationPrefs();
+ private static int getNextRetry(int subId) {
+ final BuglePrefs prefs = Factory.get().getSubscriptionPrefs(subId);
final int retryAttempt =
prefs.getInt(BuglePrefsKeys.PROCESS_PENDING_MESSAGES_RETRY_COUNT, 0) + 1;
prefs.putInt(BuglePrefsKeys.PROCESS_PENDING_MESSAGES_RETRY_COUNT, retryAttempt);
@@ -215,21 +212,27 @@
/**
* Read from the DB and determine if there are any messages we should process
+ *
+ * @param subId the subId
* @return true if we have pending messages
*/
- private static boolean getHavePendingMessages() {
+ private static boolean getHavePendingMessages(final int subId) {
final DatabaseWrapper db = DataModel.get().getDatabase();
final long now = System.currentTimeMillis();
+ final String selfId = ParticipantData.getParticipantId(db, subId);
+ if (selfId == null) {
+ // This could be happened before refreshing participant.
+ LogUtil.w(TAG, "ProcessPendingMessagesAction: selfId is null for subId " + subId);
+ return false;
+ }
- for (int subId : getActiveSubscriptionIds()) {
- final String toSendMessageId = findNextMessageToSend(db, now, subId);
- if (toSendMessageId != null) {
+ final String toSendMessageId = findNextMessageToSend(db, now, selfId);
+ if (toSendMessageId != null) {
+ return true;
+ } else {
+ final String toDownloadMessageId = findNextMessageToDownload(db, now, selfId);
+ if (toDownloadMessageId != null) {
return true;
- } else {
- final String toDownloadMessageId = findNextMessageToDownload(db, now, subId);
- if (toDownloadMessageId != null) {
- return true;
- }
}
}
// Messages may be in the process of sending/downloading even when there are no pending
@@ -237,190 +240,148 @@
return false;
}
- private static int[] getActiveSubscriptionIds() {
- if (!OsUtil.isAtLeastL_MR1()) {
- return new int[] { ParticipantData.DEFAULT_SELF_SUB_ID };
- }
- List<SubscriptionInfo> subscriptions = PhoneUtils.getDefault().toLMr1()
- .getActiveSubscriptionInfoList();
-
- int numSubs = subscriptions.size();
- int[] result = new int[numSubs];
- for (int i = 0; i < numSubs; i++) {
- result[i] = subscriptions.get(i).getSubscriptionId();
- }
- return result;
- }
-
/**
* Queue any pending actions
+ *
* @param actionState
* @return true if action queued (or no actions to queue) else false
*/
private boolean queueActions(final Action processingAction) {
final DatabaseWrapper db = DataModel.get().getDatabase();
final long now = System.currentTimeMillis();
- boolean succeeded = false;
+ boolean succeeded = true;
+ final int subId = processingAction.actionParameters
+ .getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
- // Will queue no more than one message per subscription to send plus one message to download
+ LogUtil.i(TAG, "ProcessPendingMessagesAction: Start queueing for subId " + subId);
+
+ final String selfId = ParticipantData.getParticipantId(db, subId);
+ if (selfId == null) {
+ // This could be happened before refreshing participant.
+ LogUtil.w(TAG, "ProcessPendingMessagesAction: selfId is null");
+ return false;
+ }
+
+ // Will queue no more than one message to send plus one message to download
// This keeps outgoing messages "in order" but allow downloads to happen even if sending
- // gets blocked until messages time out. Manual resend bumps messages to head of queue.
- for (int subId : getActiveSubscriptionIds()) {
- final String toSendMessageId = findNextMessageToSend(db, now, subId);
- final String toDownloadMessageId = findNextMessageToDownload(db, now, subId);
- if (toSendMessageId != null) {
- LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message " + toSendMessageId
- + " for sending");
- // This could queue nothing
- if (!SendMessageAction.queueForSendInBackground(toSendMessageId,
- processingAction)) {
- LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
- + toSendMessageId + " for sending");
- } else {
- succeeded = true;
- }
+ // gets blocked until messages time out. Manual resend bumps messages to head of queue.
+ final String toSendMessageId = findNextMessageToSend(db, now, selfId);
+ final String toDownloadMessageId = findNextMessageToDownload(db, now, selfId);
+ if (toSendMessageId != null) {
+ LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message " + toSendMessageId
+ + " for sending");
+ // This could queue nothing
+ if (!SendMessageAction.queueForSendInBackground(toSendMessageId, processingAction)) {
+ LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
+ + toSendMessageId + " for sending");
+ succeeded = false;
}
- if (toDownloadMessageId != null) {
- LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message "
+ }
+ if (toDownloadMessageId != null) {
+ LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message " + toDownloadMessageId
+ + " for download");
+ // This could queue nothing
+ if (!DownloadMmsAction.queueMmsForDownloadInBackground(toDownloadMessageId,
+ processingAction)) {
+ LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
+ toDownloadMessageId + " for download");
- // This could queue nothing
- if (!DownloadMmsAction.queueMmsForDownloadInBackground(toDownloadMessageId,
- processingAction)) {
- LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
- + toDownloadMessageId + " for download");
- } else {
- succeeded = true;
- }
-
+ succeeded = false;
}
- if (toSendMessageId == null && toDownloadMessageId == null) {
- if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
- LogUtil.d(TAG, "ProcessPendingMessagesAction: No messages to send or download");
- }
- succeeded = true;
- }
+ }
+ if (toSendMessageId == null && toDownloadMessageId == null) {
+ LogUtil.i(TAG, "ProcessPendingMessagesAction: No messages to send or download");
}
return succeeded;
}
@Override
protected Object executeAction() {
+ final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
// If triggered by alarm will not have unregistered yet
- unregister();
+ unregister(subId);
if (PhoneUtils.getDefault().isDefaultSmsApp()) {
- queueActions(this);
+ if (!queueActions(this)) {
+ LogUtil.v(TAG, "ProcessPendingMessagesAction: rescheduling");
+ // TODO: Need to clear retry count here?
+ scheduleProcessPendingMessagesAction(true /* failed */, this);
+ }
} else {
if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
LogUtil.v(TAG, "ProcessPendingMessagesAction: Not default SMS app; rescheduling");
}
- scheduleProcessPendingMessagesAction(true, this);
+ scheduleProcessPendingMessagesAction(true /* failed */, this);
}
return null;
}
- private static String prefixColumnWithTable(final String tableName, final String column) {
- return tableName + "." + column;
- }
-
- private static String[] prefixProjectionWithTable(final String tableName,
- final String[] projection) {
- String[] result = new String[projection.length];
- for (int i = 0; i < projection.length; i++) {
- result[i] = prefixColumnWithTable(tableName, projection[i]);
- }
- return result;
- }
-
private static String findNextMessageToSend(final DatabaseWrapper db, final long now,
- final int subId) {
+ final String selfId) {
String toSendMessageId = null;
- db.beginTransaction();
- Cursor sending = null;
Cursor cursor = null;
int sendingCnt = 0;
int pendingCnt = 0;
int failedCnt = 0;
+ db.beginTransaction();
try {
- String[] projection = prefixProjectionWithTable(DatabaseHelper.MESSAGES_TABLE,
- MessageData.getProjection());
- String subIdClause =
- prefixColumnWithTable(DatabaseHelper.MESSAGES_TABLE,
- MessageColumns.SELF_PARTICIPANT_ID)
- + " = "
- + prefixColumnWithTable(DatabaseHelper.PARTICIPANTS_TABLE,
- ParticipantColumns._ID)
- + " AND " + ParticipantColumns.SUB_ID + " =?";
-
// First check to see if we have any messages already sending
- sending = db.query(DatabaseHelper.MESSAGES_TABLE + ","
- + DatabaseHelper.PARTICIPANTS_TABLE,
- projection,
- DatabaseHelper.MessageColumns.STATUS + " IN (?, ?)"
- + " AND " + subIdClause,
- new String[]{Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_SENDING),
- Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_RESENDING),
- Integer.toString(subId)},
- null,
- null,
- DatabaseHelper.MessageColumns.RECEIVED_TIMESTAMP + " ASC");
- final boolean messageCurrentlySending = sending.moveToNext();
- sendingCnt = sending.getCount();
+ sendingCnt = (int) db.queryNumEntries(DatabaseHelper.MESSAGES_TABLE,
+ DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+ + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =? ",
+ new String[] {
+ Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_SENDING),
+ Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_RESENDING),
+ selfId}
+ );
+
// Look for messages we could send
- final ContentValues values = new ContentValues();
- values.put(DatabaseHelper.MessageColumns.STATUS,
- MessageData.BUGLE_STATUS_OUTGOING_FAILED);
- cursor = db.query(DatabaseHelper.MESSAGES_TABLE + ","
- + DatabaseHelper.PARTICIPANTS_TABLE,
- projection,
- DatabaseHelper.MessageColumns.STATUS + " IN ("
- + MessageData.BUGLE_STATUS_OUTGOING_YET_TO_SEND + ","
- + MessageData.BUGLE_STATUS_OUTGOING_AWAITING_RETRY + ")"
- + " AND " + subIdClause,
- new String[]{Integer.toString(subId)},
+ cursor = db.query(DatabaseHelper.MESSAGES_TABLE,
+ MessageData.getProjection(),
+ DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+ + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =? ",
+ new String[] {
+ Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_YET_TO_SEND),
+ Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_AWAITING_RETRY),
+ selfId
+ },
null,
null,
DatabaseHelper.MessageColumns.RECEIVED_TIMESTAMP + " ASC");
pendingCnt = cursor.getCount();
+ final ContentValues values = new ContentValues();
+ values.put(DatabaseHelper.MessageColumns.STATUS,
+ MessageData.BUGLE_STATUS_OUTGOING_FAILED);
+
+ // Prior to L_MR1, isActiveSubscription is true always
+ boolean isActiveSubscription = true;
+ if (OsUtil.isAtLeastL_MR1()) {
+ final ParticipantData messageSelf =
+ BugleDatabaseOperations.getExistingParticipant(db, selfId);
+ if (messageSelf == null || !messageSelf.isActiveSubscription()) {
+ isActiveSubscription = false;
+ }
+ }
while (cursor.moveToNext()) {
final MessageData message = new MessageData();
message.bind(cursor);
- if (message.getInResendWindow(now)) {
- // If no messages currently sending
- if (!messageCurrentlySending) {
- // Resend this message
- toSendMessageId = message.getMessageId();
- // Before queuing the message for resending, check if the message's self is
- // active. If not, switch back to the system's default subscription.
- if (OsUtil.isAtLeastL_MR1()) {
- final ParticipantData messageSelf = BugleDatabaseOperations
- .getExistingParticipant(db, message.getSelfId());
- if (messageSelf == null || !messageSelf.isActiveSubscription()) {
- final ParticipantData defaultSelf = BugleDatabaseOperations
- .getOrCreateSelf(db, PhoneUtils.getDefault()
- .getDefaultSmsSubscriptionId());
- if (defaultSelf != null) {
- message.bindSelfId(defaultSelf.getId());
- final ContentValues selfValues = new ContentValues();
- selfValues.put(MessageColumns.SELF_PARTICIPANT_ID,
- defaultSelf.getId());
- BugleDatabaseOperations.updateMessageRow(db,
- message.getMessageId(), selfValues);
- MessagingContentProvider.notifyMessagesChanged(
- message.getConversationId());
- }
- }
- }
- }
- break;
- } else {
+
+ // Mark this message as failed if the message's self is inactive or the message is
+ // outside of resend window
+ if (!isActiveSubscription || !message.getInResendWindow(now)) {
failedCnt++;
// Mark message as failed
BugleDatabaseOperations.updateMessageRow(db, message.getMessageId(), values);
MessagingContentProvider.notifyMessagesChanged(message.getConversationId());
+ } else {
+ // If no messages currently sending
+ if (sendingCnt == 0) {
+ // Send this message
+ toSendMessageId = message.getMessageId();
+ }
+ break;
}
}
db.setTransactionSuccessful();
@@ -429,9 +390,6 @@
if (cursor != null) {
cursor.close();
}
- if (sending != null) {
- sending.close();
- }
}
if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
@@ -445,48 +403,33 @@
}
private static String findNextMessageToDownload(final DatabaseWrapper db, final long now,
- final int subId) {
+ final String selfId) {
String toDownloadMessageId = null;
- db.beginTransaction();
Cursor cursor = null;
int downloadingCnt = 0;
int pendingCnt = 0;
+ db.beginTransaction();
try {
- String[] projection = prefixProjectionWithTable(DatabaseHelper.MESSAGES_TABLE,
- MessageData.getProjection());
- String subIdClause =
- prefixColumnWithTable(DatabaseHelper.MESSAGES_TABLE,
- MessageColumns.SELF_PARTICIPANT_ID)
- + " = "
- + prefixColumnWithTable(DatabaseHelper.PARTICIPANTS_TABLE,
- ParticipantColumns._ID)
- + " AND " + ParticipantColumns.SUB_ID + " =?";
-
// First check if we have any messages already downloading
- downloadingCnt = (int) db.queryNumEntries(DatabaseHelper.MESSAGES_TABLE
- + "," + DatabaseHelper.PARTICIPANTS_TABLE,
- DatabaseHelper.MessageColumns.STATUS + " IN (?, ?)"
- + " AND " + subIdClause,
+ downloadingCnt = (int) db.queryNumEntries(DatabaseHelper.MESSAGES_TABLE,
+ DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+ + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =?",
new String[] {
Integer.toString(MessageData.BUGLE_STATUS_INCOMING_AUTO_DOWNLOADING),
Integer.toString(MessageData.BUGLE_STATUS_INCOMING_MANUAL_DOWNLOADING),
- Integer.toString(subId)
+ selfId
});
// TODO: This query is not actually needed if downloadingCnt == 0.
- cursor = db.query(DatabaseHelper.MESSAGES_TABLE + ","
- + DatabaseHelper.PARTICIPANTS_TABLE,
- projection,
- DatabaseHelper.MessageColumns.STATUS + " =? OR "
- + DatabaseHelper.MessageColumns.STATUS + " =?"
- + " AND " + subIdClause,
+ cursor = db.query(DatabaseHelper.MESSAGES_TABLE,
+ MessageData.getProjection(),
+ DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+ + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =? ",
new String[]{
- Integer.toString(
- MessageData.BUGLE_STATUS_INCOMING_RETRYING_AUTO_DOWNLOAD),
- Integer.toString(
- MessageData.BUGLE_STATUS_INCOMING_RETRYING_MANUAL_DOWNLOAD),
- Integer.toString(
- subId)
+ Integer.toString(MessageData.BUGLE_STATUS_INCOMING_RETRYING_AUTO_DOWNLOAD),
+ Integer.toString(
+ MessageData.BUGLE_STATUS_INCOMING_RETRYING_MANUAL_DOWNLOAD),
+ selfId
},
null,
null,
diff --git a/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java b/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java
index f408e47..489a0f1 100644
--- a/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java
@@ -207,6 +207,8 @@
if (message == null) {
LogUtil.w(TAG, "ProcessSentMessageAction: Sent message " + messageId
+ " missing from local database");
+ ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(
+ true /* failed */, processingAction);
return;
}
final String conversationId = message.getConversationId();
diff --git a/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java b/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java
index 5ffb35d..cb7a35d 100644
--- a/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java
@@ -44,6 +44,7 @@
private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
private static final String KEY_MESSAGE_VALUES = "message_values";
+ private static final String KEY_SUB_ID = "sub_id";
/**
* Create a message received from a particular number in a particular conversation
@@ -158,6 +159,7 @@
+ " in conversation " + message.getConversationId()
+ ", uri = " + messageUri);
+ actionParameters.putInt(KEY_SUB_ID, subId);
ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this);
} else {
if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
diff --git a/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java b/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java
index e899b0c..69c34da 100644
--- a/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java
+++ b/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java
@@ -36,7 +36,9 @@
*/
public class RedownloadMmsAction extends Action implements Parcelable {
private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
- private static final int REQUEST_CODE_PENDING_INTENT = 102;
+ private static final int REQUEST_CODE_PENDING_INTENT = 101;
+
+ private static final String KEY_SUB_ID = "sub_id";
/**
* Download an MMS message
@@ -90,7 +92,8 @@
BugleDatabaseOperations.updateMessageRow(db, message.getMessageId(), values);
MessagingContentProvider.notifyMessagesChanged(message.getConversationId());
-
+ actionParameters.putInt(KEY_SUB_ID,
+ BugleDatabaseOperations.getSelfSubscriptionId(db, message.getSelfId()));
// Whether we succeeded or failed we will check and maybe schedule some more work
ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this);
} else {
diff --git a/src/com/android/messaging/datamodel/action/ResendMessageAction.java b/src/com/android/messaging/datamodel/action/ResendMessageAction.java
index 2201965..b442ae6 100644
--- a/src/com/android/messaging/datamodel/action/ResendMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/ResendMessageAction.java
@@ -34,6 +34,8 @@
public class ResendMessageAction extends Action implements Parcelable {
private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
+ private static final String KEY_SUB_ID = "sub_id";
+
/**
* Manual send of existing message (no listener)
*/
@@ -78,8 +80,6 @@
final ContentValues values = new ContentValues();
values.put(MessageColumns.STATUS, MessageData.BUGLE_STATUS_OUTGOING_YET_TO_SEND);
- values.put(MessageColumns.RECEIVED_TIMESTAMP, timestamp);
- values.put(MessageColumns.SENT_TIMESTAMP, timestamp);
values.put(MessageColumns.RETRY_START_TIMESTAMP, timestamp);
// Row must exist as was just loaded above (on ActionService thread)
@@ -87,6 +87,9 @@
MessagingContentProvider.notifyMessagesChanged(message.getConversationId());
+ actionParameters.putInt(KEY_SUB_ID,
+ BugleDatabaseOperations.getSelfSubscriptionId(db, message.getSelfId()));
+
// Whether we succeeded or failed we will check and maybe schedule some more work
ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this);
diff --git a/src/com/android/messaging/datamodel/action/SendMessageAction.java b/src/com/android/messaging/datamodel/action/SendMessageAction.java
index d7ebe8f..44c661d 100644
--- a/src/com/android/messaging/datamodel/action/SendMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/SendMessageAction.java
@@ -44,7 +44,6 @@
* Action used to send an outgoing message. It writes MMS messages to the telephony db
* ({@link InsertNewMessageAction}) writes SMS messages to the telephony db). It also
* initiates the actual sending. It will all be used for re-sending a failed message.
- * NOTE: This action must queue a ProcessPendingMessagesAction when it is done (success or failure).
* <p>
* This class is public (not package-private) because the SMS/MMS (e.g. MmsUtils) classes need to
* access the EXTRA_* fields for setting up the 'sent' pending intent.
@@ -96,13 +95,17 @@
private boolean queueAction(final String messageId, final Action processingAction) {
actionParameters.putString(KEY_MESSAGE_ID, messageId);
- final long timestamp = System.currentTimeMillis();
final DatabaseWrapper db = DataModel.get().getDatabase();
final MessageData message = BugleDatabaseOperations.readMessage(db, messageId);
// Check message can be resent
if (message != null && message.canSendMessage()) {
- final boolean isSms = (message.getProtocol() == MessageData.PROTOCOL_SMS);
+ final boolean isSms = message.getIsSms();
+ long timestamp = System.currentTimeMillis();
+ if (!isSms) {
+ // MMS expects timestamp rounded to nearest second
+ timestamp = 1000 * ((timestamp + 500) / 1000);
+ }
final ParticipantData self = BugleDatabaseOperations.getExistingParticipant(
db, message.getSelfId());
@@ -111,8 +114,13 @@
// Update message status
if (message.getYetToSend()) {
- // Initial sending of message
- message.markMessageSending(timestamp);
+ if (message.getReceivedTimeStamp() == message.getRetryStartTimestamp()) {
+ // Initial sending of message
+ message.markMessageSending(timestamp);
+ } else {
+ // Manual resend of message
+ message.markMessageManualResend(timestamp);
+ }
} else {
// Automatic resend of message
message.markMessageResending(timestamp);
@@ -294,9 +302,6 @@
MmsUtils.MMS_REQUEST_MANUAL_RETRY, MessageData.RAW_TELEPHONY_STATUS_UNDEFINED,
isSms, this, subId, resultCode, httpStatusCode);
- // Whether we succeeded or failed we will check and maybe schedule some more work
- ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(true, this);
-
return null;
}
diff --git a/src/com/android/messaging/datamodel/data/ConversationData.java b/src/com/android/messaging/datamodel/data/ConversationData.java
index 55d5bfc..704e46f 100644
--- a/src/com/android/messaging/datamodel/data/ConversationData.java
+++ b/src/com/android/messaging/datamodel/data/ConversationData.java
@@ -780,8 +780,8 @@
}
/**
- * A dummy implementation of {@link ConversationDataListener} so that subclasses may opt to
- * implement some, but not all, of the interface methods.
+ * A placeholder implementation of {@link ConversationDataListener} so that subclasses may opt
+ * to implement some, but not all, of the interface methods.
*/
public static class SimpleConversationDataListener implements ConversationDataListener {
diff --git a/src/com/android/messaging/datamodel/data/ConversationMessageData.java b/src/com/android/messaging/datamodel/data/ConversationMessageData.java
index 19e1b97..2a85277 100644
--- a/src/com/android/messaging/datamodel/data/ConversationMessageData.java
+++ b/src/com/android/messaging/datamodel/data/ConversationMessageData.java
@@ -523,7 +523,8 @@
}
public boolean getIsSendComplete() {
- return mStatus == MessageData.BUGLE_STATUS_OUTGOING_COMPLETE;
+ return (mStatus == MessageData.BUGLE_STATUS_OUTGOING_COMPLETE
+ || mStatus == MessageData.BUGLE_STATUS_OUTGOING_DELIVERED);
}
public String getSenderFullName() {
@@ -575,8 +576,9 @@
public boolean getCanForwardMessage() {
// Even for outgoing messages, we only allow forwarding if the message has finished sending
// as media often has issues when send isn't complete
- return (mStatus == MessageData.BUGLE_STATUS_OUTGOING_COMPLETE ||
- mStatus == MessageData.BUGLE_STATUS_INCOMING_COMPLETE);
+ return (mStatus == MessageData.BUGLE_STATUS_OUTGOING_COMPLETE
+ || mStatus == MessageData.BUGLE_STATUS_OUTGOING_DELIVERED
+ || mStatus == MessageData.BUGLE_STATUS_INCOMING_COMPLETE);
}
public boolean getCanCopyMessageToClipboard() {
diff --git a/src/com/android/messaging/datamodel/data/DraftMessageData.java b/src/com/android/messaging/datamodel/data/DraftMessageData.java
index 7a7199a..f63c27f 100644
--- a/src/com/android/messaging/datamodel/data/DraftMessageData.java
+++ b/src/com/android/messaging/datamodel/data/DraftMessageData.java
@@ -339,11 +339,23 @@
*/
private boolean addOneAttachmentNoNotify(final MessagePartData attachment) {
Assert.isTrue(attachment.isAttachment());
+ // Check duplication.
+ for (final MessagePartData existingAttachment : mAttachments) {
+ if (existingAttachment.getContentUri().equals(attachment.getContentUri())) {
+ // Destroy existing attachment and replace with new attachment instead of destroying
+ // new one so that mSelectedImages in GalleryGridView could be maintained correctly.
+ mAttachments.remove(existingAttachment);
+ existingAttachment.destroyAsync();
+ addAttachment(attachment, null /*pendingAttachment*/);
+ return false;
+ }
+ }
+
final boolean reachedLimit = getAttachmentCount() >= getAttachmentLimit();
- if (reachedLimit || containsAttachment(attachment.getContentUri())) {
- // Never go over the limit. Never add duplicated attachments.
+ if (reachedLimit) {
+ // Never go over the limit.
attachment.destroyAsync();
- return reachedLimit;
+ return true;
} else {
addAttachment(attachment, null /*pendingAttachment*/);
return false;
diff --git a/src/com/android/messaging/datamodel/data/MessageData.java b/src/com/android/messaging/datamodel/data/MessageData.java
index cb5311e..b2cab48 100644
--- a/src/com/android/messaging/datamodel/data/MessageData.java
+++ b/src/com/android/messaging/datamodel/data/MessageData.java
@@ -297,9 +297,11 @@
/**
* Create a message not yet associated with a particular conversation
*/
- public static MessageData createSharedMessage(final String messageText) {
+ public static MessageData createSharedMessage(final String messageText,
+ final String subjectText) {
final MessageData message = new MessageData();
message.mStatus = BUGLE_STATUS_OUTGOING_DRAFT;
+ message.mMmsSubject = subjectText;
if (!TextUtils.isEmpty(messageText)) {
message.mParts.add(MessagePartData.createTextMessagePart(messageText));
}
@@ -580,7 +582,7 @@
// primary user.
return false;
}
- // Should show option for manual download iff status is manual download or failed
+ // Should show option for manual download if status is manual download or failed
return (status == BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED ||
status == BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD ||
// If debug is enabled, allow to download an expired or unavailable message.
@@ -594,7 +596,7 @@
// primary user.
return false;
}
- // Can download iff status is retrying auto/manual downloading
+ // Can download if status is retrying auto/manual downloading
return (mStatus == BUGLE_STATUS_INCOMING_RETRYING_MANUAL_DOWNLOAD ||
mStatus == BUGLE_STATUS_INCOMING_RETRYING_AUTO_DOWNLOAD);
}
@@ -605,7 +607,7 @@
// primary user.
return false;
}
- // Can redownload iff status is manual download not started or download failed
+ // Can redownload if status is manual download not started or download failed
return (mStatus == BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED ||
mStatus == BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD ||
// If debug is enabled, allow to download an expired or unavailable message.
@@ -614,12 +616,12 @@
}
static boolean getShowResendMessage(final int status) {
- // Should show option to resend iff status is failed
+ // Should show option to resend if status is failed
return (status == BUGLE_STATUS_OUTGOING_FAILED);
}
static boolean getOneClickResendMessage(final int status, final int rawStatus) {
- // Should show option to resend iff status is failed
+ // Should show option to resend if status is failed
return (status == BUGLE_STATUS_OUTGOING_FAILED
&& rawStatus == RAW_TELEPHONY_STATUS_UNDEFINED);
}
diff --git a/src/com/android/messaging/datamodel/data/ParticipantData.java b/src/com/android/messaging/datamodel/data/ParticipantData.java
index 51e6ee2..95c74e2 100644
--- a/src/com/android/messaging/datamodel/data/ParticipantData.java
+++ b/src/com/android/messaging/datamodel/data/ParticipantData.java
@@ -22,10 +22,12 @@
import android.graphics.Color;
import android.os.Parcel;
import android.os.Parcelable;
-import androidx.appcompat.mms.MmsManager;
import android.telephony.SubscriptionInfo;
import android.text.TextUtils;
+import androidx.appcompat.mms.MmsManager;
+import androidx.collection.ArrayMap;
+
import com.android.ex.chips.RecipientEntry;
import com.android.messaging.Factory;
import com.android.messaging.R;
@@ -41,6 +43,10 @@
* A class that encapsulates all of the data for a specific participant in a conversation.
*/
public class ParticipantData implements Parcelable {
+
+ private static final ArrayMap<Integer, String> sSubIdtoParticipantIdCache =
+ new ArrayMap<Integer, String>();
+
// We always use -1 as default/invalid sub id although system may give us anything negative
public static final int DEFAULT_SELF_SUB_ID = MmsManager.DEFAULT_SUB_ID;
@@ -282,6 +288,34 @@
return pd;
}
+ public static String getParticipantId(final DatabaseWrapper db, final int subId) {
+ String id;
+ synchronized (sSubIdtoParticipantIdCache) {
+ id = sSubIdtoParticipantIdCache.get(subId);
+ }
+
+ if (id != null) {
+ return id;
+ }
+
+ try (final Cursor cursor =
+ db.query(DatabaseHelper.PARTICIPANTS_TABLE,
+ new String[] {ParticipantColumns._ID},
+ ParticipantColumns.SUB_ID + " =?",
+ new String[] {Integer.toString(subId)}, null, null, null)) {
+
+ if (cursor.moveToFirst()) {
+ // We found an existing participant in the database
+ id = cursor.getString(0);
+ synchronized (sSubIdtoParticipantIdCache) {
+ // Add it to the cache for next time
+ sSubIdtoParticipantIdCache.put(subId, id);
+ }
+ }
+ }
+ return id;
+ }
+
private void maybeSetupUnknownSender() {
if (isUnknownSender()) {
// Because your locale may change, we setup the display string for the unknown sender
diff --git a/src/com/android/messaging/datamodel/media/BindableMediaRequest.java b/src/com/android/messaging/datamodel/media/BindableMediaRequest.java
index 36521d5..d979323 100644
--- a/src/com/android/messaging/datamodel/media/BindableMediaRequest.java
+++ b/src/com/android/messaging/datamodel/media/BindableMediaRequest.java
@@ -19,7 +19,7 @@
import com.android.messaging.datamodel.media.MediaResourceManager.MediaResourceLoadListener;
/**
- * The {@link MediaRequest} interface is threading-model-blind, allowing the implementations to
+ * The {@link MediaRequest} interface is threading-model-oblivious, allowing the implementations to
* be processed synchronously or asynchronously.
* This is a {@link MediaRequest} implementation that includes functionalities such as binding and
* event callbacks for multi-threaded media request processing.
diff --git a/src/com/android/messaging/datamodel/media/MediaRequest.java b/src/com/android/messaging/datamodel/media/MediaRequest.java
index 703671b..78ae85e 100644
--- a/src/com/android/messaging/datamodel/media/MediaRequest.java
+++ b/src/com/android/messaging/datamodel/media/MediaRequest.java
@@ -21,7 +21,7 @@
* Keeps track of a media loading request. MediaResourceManager uses this interface to load, encode,
* decode, and cache different types of media resource.
*
- * This interface defines a media request class that's threading-model-blind. Wrapper classes
+ * This interface defines a media request class that's threading-model-oblivious. Wrapper classes
* (such as {@link AsyncMediaRequestWrapper} wraps around any base media request to offer async
* extensions).
*/
@@ -67,4 +67,4 @@
* Returns the descriptor defining the request.
*/
MediaRequestDescriptor<T> getDescriptor();
-}
\ No newline at end of file
+}
diff --git a/src/com/android/messaging/mmslib/pdu/PduComposer.java b/src/com/android/messaging/mmslib/pdu/PduComposer.java
index f318382..ace49d1 100644
--- a/src/com/android/messaging/mmslib/pdu/PduComposer.java
+++ b/src/com/android/messaging/mmslib/pdu/PduComposer.java
@@ -1080,7 +1080,7 @@
}
if (dataLength != (attachment.getLength() - headerLength)) {
- throw new RuntimeException("BUG: Length sanity check failed");
+ throw new RuntimeException("BUG: Length check failed");
}
mStack.pop();
diff --git a/src/com/android/messaging/mmslib/pdu/PduPersister.java b/src/com/android/messaging/mmslib/pdu/PduPersister.java
index 1139aa1..436867a 100644
--- a/src/com/android/messaging/mmslib/pdu/PduPersister.java
+++ b/src/com/android/messaging/mmslib/pdu/PduPersister.java
@@ -52,6 +52,7 @@
import com.android.messaging.util.ContentType;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.OsUtil;
+import com.android.messaging.util.UriUtil;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -1018,8 +1019,7 @@
String path = null;
if (null != uri) {
final String scheme = uri.getScheme();
- if (null == scheme || scheme.equals("") ||
- scheme.equals(ContentResolver.SCHEME_FILE)) {
+ if (null == scheme || scheme.equals("") || UriUtil.isFileUri(uri)) {
path = uri.getPath();
} else if (scheme.equals("http")) {
@@ -1415,11 +1415,11 @@
// For received messages (whether group MMS is enabled or not) we want to
// associate this message with the thread composed of all the recipients
- // EXCLUDING our own number. This includes the person who sent the
- // message (the FROM field above) in addition to the other people the message
- // was addressed TO (or CC fields to address group messaging compatibility
- // issues with devices that place numbers in this field). Typically our own
- // number is in the TO/CC field so we have to remove it in loadRecipients.
+ // EXCLUDING our own number. This includes the person who sent the message
+ // (the FROM field above) in addition to the other people the message was
+ // addressed TO (or CC fields to address group messaging compatibility issues
+ // with devices that place numbers in this field). Typically our own number is
+ // in the TO/CC field so we have to remove it in checkAndLoadToCcRecipients.
checkAndLoadToCcRecipients(recipients, addressMap, subPhoneNumber);
break;
case PduHeaders.MESSAGE_TYPE_SEND_REQ:
@@ -1440,7 +1440,7 @@
// Save parts first to avoid inconsistent message is loaded
// while saving the parts.
- final long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.
+ final long placeholderId = System.currentTimeMillis(); // Placeholder ID of the msg.
// Figure out if this PDU is a text-only message
boolean textOnly = true;
@@ -1463,7 +1463,7 @@
}
for (int i = 0; i < partsNum; i++) {
final PduPart part = body.getPart(i);
- persistPart(part, dummyId, preOpenedFiles);
+ persistPart(part, placeholderId, preOpenedFiles);
// If we've got anything besides text/plain or SMIL part, then we've got
// an mms message with some other type of attachment.
@@ -1501,14 +1501,14 @@
throw new MmsException("persist() failed: return null.");
}
// Get the real ID of the PDU and update all parts which were
- // saved with the dummy ID.
+ // saved with the placeholder ID.
msgId = ContentUris.parseId(res);
}
values = new ContentValues(1);
values.put(Part.MSG_ID, msgId);
SqliteWrapper.update(mContext, mContentResolver,
- Uri.parse("content://mms/" + dummyId + "/part"),
+ Uri.parse("content://mms/" + placeholderId + "/part"),
values, null, null);
// We should return the longest URI of the persisted PDU, for
// example, if input URI is "content://mms/inbox" and the _ID of
@@ -1582,9 +1582,17 @@
}
}
}
+
+ // If selfNumber is unavailable and there is only a single address in all TO and CC, we can
+ // skip adding it into recipients as assuming it is my own phone number.
+ final boolean isSelfNumberUnavailable = TextUtils.isEmpty(selfNumber);
+ if (isSelfNumberUnavailable && numbers.size() == 1) {
+ return;
+ }
+
for (final String number : numbers) {
// Only add numbers which aren't my own number.
- if (TextUtils.isEmpty(selfNumber) || !PhoneNumberUtils.compare(number, selfNumber)) {
+ if (isSelfNumberUnavailable || !PhoneNumberUtils.compare(number, selfNumber)) {
if (!recipients.contains(number)) {
// Only add numbers which aren't already included.
recipients.add(number);
diff --git a/src/com/android/messaging/receiver/SendStatusReceiver.java b/src/com/android/messaging/receiver/SendStatusReceiver.java
index c1b2bca..8d2e10f 100644
--- a/src/com/android/messaging/receiver/SendStatusReceiver.java
+++ b/src/com/android/messaging/receiver/SendStatusReceiver.java
@@ -87,7 +87,7 @@
final String format = intent.getStringExtra("format");
status = smsMessage.getStatus();
// Simple matching up CDMA status with GSM status.
- if (SmsMessage.FORMAT_3GPP2.equals(format)) {
+ if ("3gpp2".equals(format)) {
final int errorClass = (status >> 24) & 0x03;
final int statusCode = (status >> 16) & 0x3f;
switch (errorClass) {
diff --git a/src/com/android/messaging/sms/MmsSmsUtils.java b/src/com/android/messaging/sms/MmsSmsUtils.java
index 1a0ef99..7719359 100644
--- a/src/com/android/messaging/sms/MmsSmsUtils.java
+++ b/src/com/android/messaging/sms/MmsSmsUtils.java
@@ -101,6 +101,14 @@
}
/**
+ * This pattern is intended for searching for carrier specific phone numbers starting with star
+ * sign and digits such as the voice mail number.
+ * (e.g. *20 Chile Claro)
+ */
+ private static final Pattern PHONE_NUMBER_STARTING_WITH_STAR_PATTERN =
+ Pattern.compile("\\*[0-9]+");
+
+ /**
* Returns true if the number is a Phone number
*
* @param number the input number to be tested
@@ -111,8 +119,12 @@
return false;
}
- final Matcher match = Patterns.PHONE.matcher(number);
- return match.matches();
+ Matcher match = Patterns.PHONE.matcher(number);
+ if (!match.matches()) {
+ match = PHONE_NUMBER_STARTING_WITH_STAR_PATTERN.matcher(number);
+ return match.matches();
+ }
+ return true;
}
/**
diff --git a/src/com/android/messaging/sms/MmsUtils.java b/src/com/android/messaging/sms/MmsUtils.java
index df0db34..32f0616 100644
--- a/src/com/android/messaging/sms/MmsUtils.java
+++ b/src/com/android/messaging/sms/MmsUtils.java
@@ -27,8 +27,6 @@
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.media.MediaMetadataRetriever;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
@@ -1113,51 +1111,6 @@
return subject;
}
- // return a semicolon separated list of phone numbers from a smsto: uri.
- public static String getSmsRecipients(final Uri uri) {
- String recipients = uri.getSchemeSpecificPart();
- final int pos = recipients.indexOf('?');
- if (pos != -1) {
- recipients = recipients.substring(0, pos);
- }
- recipients = replaceUnicodeDigits(recipients).replace(',', ';');
- return recipients;
- }
-
- // This function was lifted from Telephony.PhoneNumberUtils because it was @hide
- /**
- * Replace arabic/unicode digits with decimal digits.
- * @param number
- * the number to be normalized.
- * @return the replaced number.
- */
- private static String replaceUnicodeDigits(final String number) {
- final StringBuilder normalizedDigits = new StringBuilder(number.length());
- for (final char c : number.toCharArray()) {
- final int digit = Character.digit(c, 10);
- if (digit != -1) {
- normalizedDigits.append(digit);
- } else {
- normalizedDigits.append(c);
- }
- }
- return normalizedDigits.toString();
- }
-
- /**
- * @return Whether the data roaming is enabled
- */
- private static boolean isDataRoamingEnabled() {
- boolean dataRoamingEnabled = false;
- final ContentResolver cr = Factory.get().getApplicationContext().getContentResolver();
- if (OsUtil.isAtLeastJB_MR1()) {
- dataRoamingEnabled = (Settings.Global.getInt(cr, Settings.Global.DATA_ROAMING, 0) != 0);
- } else {
- dataRoamingEnabled = (Settings.System.getInt(cr, Settings.System.DATA_ROAMING, 0) != 0);
- }
- return dataRoamingEnabled;
- }
-
/**
* @return Whether to auto retrieve MMS
*/
@@ -1203,7 +1156,9 @@
public static SmsMessage getSmsMessageFromDeliveryReport(final Intent intent) {
final byte[] pdu = intent.getByteArrayExtra("pdu");
final String format = intent.getStringExtra("format");
- return SmsMessage.createFromPdu(pdu, format);
+ return OsUtil.isAtLeastM()
+ ? SmsMessage.createFromPdu(pdu, format)
+ : SmsMessage.createFromPdu(pdu);
}
/**
@@ -1544,7 +1499,7 @@
cursor = SqliteWrapper.query(
context,
resolver,
- Telephony.Carriers.SIM_APN_URI,
+ Telephony.Carriers.CONTENT_URI,
TEST_CARRIERS_PROJECTION,
null/*selection*/,
null/*selectionArgs*/,
@@ -1927,10 +1882,9 @@
final long expiry, final RetrieveConf retrieveConf) {
final byte[] notificationTransactionId = stringToBytes(transactionId, "UTF-8");
Uri messageUri = null;
- int status = MMS_REQUEST_MANUAL_RETRY;
- int retrieveStatus = PDU_HEADER_VALUE_UNDEFINED;
+ final int status;
+ final int retrieveStatus = retrieveConf.getRetrieveStatus();
- retrieveStatus = retrieveConf.getRetrieveStatus();
if (retrieveStatus == PduHeaders.RETRIEVE_STATUS_OK) {
status = MMS_REQUEST_SUCCEEDED;
} else if (retrieveStatus >= PduHeaders.RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE &&
@@ -1965,17 +1919,9 @@
final Uri inboxUri = MmsUtils.insertReceivedMmsMessage(context, retrieveConf, subId,
subPhoneNumber, receivedTimestampInSeconds, expiry, transactionId);
messageUri = ContentUris.withAppendedId(Mms.CONTENT_URI, ContentUris.parseId(inboxUri));
- } else if (status == MMS_REQUEST_AUTO_RETRY) {
- // For a retry do nothing
- } else if (status == MMS_REQUEST_MANUAL_RETRY && autoDownload) {
- // Failure from autodownload - just treat like manual download
- sendNotifyResponseForMmsDownload(
- context,
- subId,
- notificationTransactionId,
- contentLocation,
- PduHeaders.STATUS_DEFERRED);
}
+ // Do nothing for MMS_REQUEST_AUTO_RETRY and MMS_REQUEST_NO_RETRY.
+
return new StatusPlusUri(status, retrieveStatus, messageUri);
}
@@ -2093,16 +2039,6 @@
return !phoneUtils.isAirplaneModeOn();
}
- public static boolean isMobileDataEnabled(final int subId) {
- final PhoneUtils phoneUtils = PhoneUtils.get(subId);
- return phoneUtils.isMobileDataEnabled();
- }
-
- public static boolean isAirplaneModeOn(final int subId) {
- final PhoneUtils phoneUtils = PhoneUtils.get(subId);
- return phoneUtils.isAirplaneModeOn();
- }
-
public static StatusPlusUri sendMmsMessage(final Context context, final int subId,
final Uri messageUri, final Bundle extras) {
int status = MMS_REQUEST_MANUAL_RETRY;
@@ -2145,7 +2081,7 @@
public static StatusPlusUri updateSentMmsMessageStatus(final Context context,
final Uri messageUri, final SendConf sendConf) {
- int status = MMS_REQUEST_MANUAL_RETRY;
+ final int status;
final int respStatus = sendConf.getResponseStatus();
final ContentValues values = new ContentValues(2);
@@ -2158,12 +2094,16 @@
messageUri, values, null, null);
if (respStatus == PduHeaders.RESPONSE_STATUS_OK) {
status = MMS_REQUEST_SUCCEEDED;
- } else if (respStatus == PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE ||
- respStatus == PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM ||
- respStatus == PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS) {
+ } else if (respStatus >= PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE
+ && respStatus < PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_FAILURE) {
+ // Only RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE and RESPONSE_STATUS_ERROR_TRANSIENT
+ // _NETWORK_PROBLEM are used in the M-Send.conf. But for others transient failures
+ // including reserved values for future purposes, it should work same as transient
+ // failure always. (OMA-MMS-ENC-V1_2, 7.2.37. X-Mms-Response-Status field)
status = MMS_REQUEST_AUTO_RETRY;
} else {
// else permanent failure
+ status = MMS_REQUEST_MANUAL_RETRY;
LogUtil.e(TAG, "MmsUtils: failed to send message; respStatus = "
+ String.format("0x%X", respStatus));
}
@@ -2662,26 +2602,6 @@
}
/**
- * The absence of a connection type.
- */
- public static final int TYPE_NONE = -1;
-
- public static int getConnectivityEventNetworkType(final Context context, final Intent intent) {
- final ConnectivityManager connMgr = (ConnectivityManager)
- context.getSystemService(Context.CONNECTIVITY_SERVICE);
- if (OsUtil.isAtLeastJB_MR1()) {
- return intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, TYPE_NONE);
- } else {
- final NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
- if (info != null) {
- return info.getType();
- }
- }
- return TYPE_NONE;
- }
-
- /**
* Dump the raw MMS data into a file
*
* @param rawPdu The raw pdu data
diff --git a/src/com/android/messaging/sms/SmsSender.java b/src/com/android/messaging/sms/SmsSender.java
index 889973f..e429995 100644
--- a/src/com/android/messaging/sms/SmsSender.java
+++ b/src/com/android/messaging/sms/SmsSender.java
@@ -64,9 +64,6 @@
private static final Random RANDOM = new Random();
- // Whether we should send multipart SMS as separate messages
- private static Boolean sSendMultipartSmsAsSeparateMessages = null;
-
/**
* Class that holds the sent status for all parts of a multipart message sending
*/
@@ -180,7 +177,7 @@
}
// This should be called from a RequestWriter queue thread
- public static SendResult sendMessage(final Context context, final int subId, String dest,
+ public static SendResult sendMessage(final Context context, final int subId, String dest,
String message, final String serviceCenter, final boolean requireDeliveryReport,
final Uri messageUri) throws SmsException {
if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
@@ -281,12 +278,8 @@
messageUri, partId, subId),
0/*flag*/));
}
- if (sSendMultipartSmsAsSeparateMessages == null) {
- sSendMultipartSmsAsSeparateMessages = MmsConfig.get(subId)
- .getSendMultipartSmsAsSeparateMessages();
- }
try {
- if (sSendMultipartSmsAsSeparateMessages) {
+ if (MmsConfig.get(subId).getSendMultipartSmsAsSeparateMessages()) {
// If multipart sms is not supported, send them as separate messages
for (int i = 0; i < messageCount; i++) {
smsManager.sendTextMessage(dest,
diff --git a/src/com/android/messaging/ui/AttachmentPreview.java b/src/com/android/messaging/ui/AttachmentPreview.java
index 7eea14b..f4465c4 100644
--- a/src/com/android/messaging/ui/AttachmentPreview.java
+++ b/src/com/android/messaging/ui/AttachmentPreview.java
@@ -57,6 +57,8 @@
private Runnable mHideRunnable;
private boolean mPendingHideCanceled;
+ private PopupTransitionAnimation mPopupTransitionAnimation;
+
private static final int CLOSE_BUTTON_REVEAL_STAGGER_MILLIS = 300;
public AttachmentPreview(final Context context, final AttributeSet attrs) {
@@ -132,6 +134,7 @@
public void run() {
// Only hide if we are didn't get overruled by showing
if (!mPendingHideCanceled) {
+ stopPopupAnimation();
mAttachmentView.removeAllViews();
setVisibility(GONE);
}
@@ -280,10 +283,19 @@
mHideRunnable.run();
}
- static void tryAnimateViewIn(final MessagePartData attachmentData, final View view) {
+ private void tryAnimateViewIn(final MessagePartData attachmentData, final View view) {
if (attachmentData instanceof MediaPickerMessagePartData) {
final Rect startRect = ((MediaPickerMessagePartData) attachmentData).getStartRect();
- new PopupTransitionAnimation(startRect, view).startAfterLayoutComplete();
+ stopPopupAnimation();
+ mPopupTransitionAnimation = new PopupTransitionAnimation(startRect, view);
+ mPopupTransitionAnimation.startAfterLayoutComplete();
+ }
+ }
+
+ private void stopPopupAnimation() {
+ if (mPopupTransitionAnimation != null) {
+ mPopupTransitionAnimation.cancel();
+ mPopupTransitionAnimation = null;
}
}
diff --git a/src/com/android/messaging/ui/MultiAttachmentLayout.java b/src/com/android/messaging/ui/MultiAttachmentLayout.java
index f620245..5bae8a5 100644
--- a/src/com/android/messaging/ui/MultiAttachmentLayout.java
+++ b/src/com/android/messaging/ui/MultiAttachmentLayout.java
@@ -33,6 +33,7 @@
import com.android.messaging.datamodel.data.PendingAttachmentData;
import com.android.messaging.datamodel.media.ImageRequestDescriptor;
import com.android.messaging.ui.AsyncImageView.AsyncImageViewDelayLoader;
+import com.android.messaging.ui.animation.PopupTransitionAnimation;
import com.android.messaging.util.AccessibilityUtil;
import com.android.messaging.util.Assert;
import com.android.messaging.util.UiUtils;
@@ -275,7 +276,11 @@
// views will slide from their previous position to their new position within the
// layout
if (i == 0) {
- AttachmentPreview.tryAnimateViewIn(attachment, attachmentWrapper.view);
+ if (attachment instanceof MediaPickerMessagePartData) {
+ final Rect startRect = ((MediaPickerMessagePartData) attachment).getStartRect();
+ new PopupTransitionAnimation(startRect, attachmentWrapper.view)
+ .startAfterLayoutComplete();
+ }
}
attachmentWrapper.needsSlideAnimation = i > 0;
}
diff --git a/src/com/android/messaging/ui/PermissionCheckActivity.java b/src/com/android/messaging/ui/PermissionCheckActivity.java
index e992a10..1b8cedb 100644
--- a/src/com/android/messaging/ui/PermissionCheckActivity.java
+++ b/src/com/android/messaging/ui/PermissionCheckActivity.java
@@ -106,10 +106,7 @@
if (requestCode == REQUIRED_PERMISSIONS_REQUEST_CODE) {
// We do not use grantResults as some of the granted permissions might have been
// revoked while the permissions dialog box was being shown for the missing permissions.
- if (OsUtil.hasRequiredPermissions()) {
- Factory.get().onRequiredPermissionsAcquired();
- redirect();
- } else {
+ if (!redirectIfNeeded()) {
final long currentTimeMillis = SystemClock.elapsedRealtime();
// If the permission request completes very quickly, it must be because the system
// automatically denied. This can happen if the user had previously denied it
@@ -130,6 +127,7 @@
return false;
}
+ Factory.get().onRequiredPermissionsAcquired();
redirect();
return true;
}
diff --git a/src/com/android/messaging/ui/SnackBarManager.java b/src/com/android/messaging/ui/SnackBarManager.java
index e107999..d5ca870 100644
--- a/src/com/android/messaging/ui/SnackBarManager.java
+++ b/src/com/android/messaging/ui/SnackBarManager.java
@@ -25,6 +25,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
+import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
@@ -86,6 +87,23 @@
}
};
+ private final OnAttachStateChangeListener mAttachStateChangeListener =
+ new OnAttachStateChangeListener() {
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ // Dismiss the PopupWindow and clear SnackBarManager state.
+ mHideHandler.removeCallbacks(mDismissRunnable);
+ mPopupWindow.dismiss();
+
+ mCurrentSnackBar = null;
+ mNextSnackBar = null;
+ mIsCurrentlyDismissing = false;
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {}
+ };
+
private final int mTranslationDurationMs;
private final Handler mHideHandler;
@@ -181,6 +199,7 @@
mPopupWindow.showAsDropDown(anchorView, 0, getRelativeOffset(snackBar));
}
+ snackBar.getParentView().addOnAttachStateChangeListener(mAttachStateChangeListener);
// Animate the toast bar into view.
placeSnackBarOffScreen(snackBar);
@@ -238,6 +257,8 @@
// PopupWindow.dismiss() will fire an IllegalArgumentException if the activity
// has already ended while we were animating
}
+ snackBar.getParentView()
+ .removeOnAttachStateChangeListener(mAttachStateChangeListener);
mCurrentSnackBar = null;
mIsCurrentlyDismissing = false;
diff --git a/src/com/android/messaging/ui/UIIntents.java b/src/com/android/messaging/ui/UIIntents.java
index b09c457..bf5edd7 100644
--- a/src/com/android/messaging/ui/UIIntents.java
+++ b/src/com/android/messaging/ui/UIIntents.java
@@ -47,6 +47,9 @@
// The request code for picking a media from the Document picker.
public static final int REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER = 1400;
+ // The request code for picking a contact card from existing Contacts apps.
+ public static final int REQUEST_PICK_CONTACT_CARD = 1500;
+
// Indicates what type of notification this applies to (See BugleNotifications:
// UPDATE_NONE, UPDATE_MESSAGES, UPDATE_ERRORS, UPDATE_ALL)
public static final String UI_INTENT_EXTRA_NOTIFICATIONS_UPDATE = "notifications_update";
@@ -166,13 +169,20 @@
public abstract void launchAddContactActivity(final Context context, final String destination);
/**
- * Launch an activity to show the document picker to pick an image/video.
+ * Launch an activity to show the document picker to pick an image/video/audio.
*
* @param fragment the requesting fragment
*/
public abstract void launchDocumentImagePicker(final Fragment fragment);
/**
+ * Launch an activity to show the contacts list to pick one.
+ *
+ * @param fragment the requesting fragment
+ */
+ public abstract void launchContactCardPicker(final Fragment fragment);
+
+ /**
* Launch an activity to show people & options for a given conversation.
*/
public abstract void launchPeopleAndOptionsActivity(final Activity context,
diff --git a/src/com/android/messaging/ui/UIIntentsImpl.java b/src/com/android/messaging/ui/UIIntentsImpl.java
index ac430cd..8a1224a 100644
--- a/src/com/android/messaging/ui/UIIntentsImpl.java
+++ b/src/com/android/messaging/ui/UIIntentsImpl.java
@@ -244,6 +244,19 @@
}
@Override
+ public void launchContactCardPicker(final Fragment fragment) {
+ final Intent intent = new Intent(Intent.ACTION_PICK);
+ intent.setType(Contacts.CONTENT_TYPE);
+
+ try {
+ fragment.startActivityForResult(intent, REQUEST_PICK_CONTACT_CARD);
+ } catch (final ActivityNotFoundException ex) {
+ LogUtil.w(LogUtil.BUGLE_TAG, "Couldn't find activity:", ex);
+ UiUtils.showToastAtBottom(R.string.activity_not_found_message);
+ }
+ }
+
+ @Override
public void launchPeopleAndOptionsActivity(final Activity activity,
final String conversationId) {
final Intent intent = new Intent(activity, PeopleAndOptionsActivity.class);
diff --git a/src/com/android/messaging/ui/appsettings/ApnEditorActivity.java b/src/com/android/messaging/ui/appsettings/ApnEditorActivity.java
index ae4b2eb..5d19a83 100644
--- a/src/com/android/messaging/ui/appsettings/ApnEditorActivity.java
+++ b/src/com/android/messaging/ui/appsettings/ApnEditorActivity.java
@@ -365,8 +365,8 @@
protected Void doInBackground(Void... params) {
ContentValues values = new ContentValues();
- // Add a dummy name "Untitled", if the user exits the screen without adding a
- // name but entered other information worth keeping.
+ // Add a placeholder name "Untitled", if the user exits the screen without
+ // adding a name but entered other information worth keeping.
values.put(Telephony.Carriers.NAME, name.length() < 1 ?
getResources().getString(R.string.untitled_apn) : name);
values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText()));
diff --git a/src/com/android/messaging/ui/appsettings/ApnPreference.java b/src/com/android/messaging/ui/appsettings/ApnPreference.java
index c5cc85a..64358eb 100644
--- a/src/com/android/messaging/ui/appsettings/ApnPreference.java
+++ b/src/com/android/messaging/ui/appsettings/ApnPreference.java
@@ -26,7 +26,6 @@
import android.widget.CompoundButton;
import android.widget.RadioButton;
import android.widget.RelativeLayout;
-import android.widget.TextView;
import com.android.messaging.R;
import com.android.messaging.datamodel.data.ParticipantData;
@@ -81,7 +80,7 @@
} else {
rb.setVisibility(View.GONE);
}
- setApnRadioButtonContentDescription(rb);
+ rb.setContentDescription(getTitle());
}
View textLayout = view.findViewById(R.id.text_layout);
@@ -92,13 +91,6 @@
return view;
}
- public void setApnRadioButtonContentDescription(final CompoundButton buttonView) {
- final View widget = (View) buttonView.getParent();
- final TextView tv = (TextView) widget.findViewById(R.id.title);
- final String apnTitle = tv.getText().toString();
- buttonView.setContentDescription(apnTitle);
- }
-
public boolean isChecked() {
return getKey().equals(mSelectedKey);
}
@@ -128,7 +120,7 @@
mCurrentChecked = null;
mSelectedKey = null;
}
- setApnRadioButtonContentDescription(buttonView);
+ buttonView.setContentDescription(getTitle());
}
public void onClick(android.view.View v) {
diff --git a/src/com/android/messaging/ui/appsettings/ApnSettingsActivity.java b/src/com/android/messaging/ui/appsettings/ApnSettingsActivity.java
index 8b4644e..13f775a 100644
--- a/src/com/android/messaging/ui/appsettings/ApnSettingsActivity.java
+++ b/src/com/android/messaging/ui/appsettings/ApnSettingsActivity.java
@@ -274,7 +274,7 @@
if (!mUnavailable) {
menu.add(0, MENU_NEW, 0,
getResources().getString(R.string.menu_new_apn))
- .setIcon(R.drawable.ic_add_gray)
+ .setIcon(R.drawable.ic_add_white)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(0, MENU_RESTORE, 0,
getResources().getString(R.string.menu_restore_default_apn))
diff --git a/src/com/android/messaging/ui/appsettings/PerSubscriptionSettingsActivity.java b/src/com/android/messaging/ui/appsettings/PerSubscriptionSettingsActivity.java
index 7ab5dc7..c2fd473 100644
--- a/src/com/android/messaging/ui/appsettings/PerSubscriptionSettingsActivity.java
+++ b/src/com/android/messaging/ui/appsettings/PerSubscriptionSettingsActivity.java
@@ -28,10 +28,12 @@
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
-import androidx.core.app.NavUtils;
import android.text.TextUtils;
import android.view.MenuItem;
+import androidx.appcompat.mms.MmsManager;
+import androidx.core.app.NavUtils;
+
import com.android.messaging.Factory;
import com.android.messaging.R;
import com.android.messaging.datamodel.ParticipantRefresh;
@@ -159,17 +161,18 @@
}
// Access Point Names (APNs)
- final Preference apnsPref = findPreference(getString(R.string.sms_apns_key));
+ final PreferenceScreen apnsScreen =
+ (PreferenceScreen) findPreference(getString(R.string.sms_apns_key));
- if (MmsUtils.useSystemApnTable() && !ApnDatabase.doesDatabaseExist()) {
- // Don't remove the ability to edit the local APN prefs if this device lets us
+ if (!MmsManager.shouldUseLegacyMms()
+ || (MmsUtils.useSystemApnTable() && !ApnDatabase.doesDatabaseExist())) {
+ // 1) Remove the ability to edit the local APN prefs if it doesn't use legacy APIs.
+ // 2) Don't remove the ability to edit the local APN prefs if this device lets us
// access the system APN, but we can't find the MCC/MNC in the APN table and we
// created the local APN table in case the MCC/MNC was in there. In other words,
// if the local APN table exists, let the user edit it.
- advancedCategory.removePreference(apnsPref);
+ advancedCategory.removePreference((Preference) apnsScreen);
} else {
- final PreferenceScreen apnsScreen = (PreferenceScreen) findPreference(
- getString(R.string.sms_apns_key));
apnsScreen.setIntent(UIIntents.get()
.getApnSettingsIntent(getPreferenceScreen().getContext(), mSubId));
}
@@ -183,7 +186,13 @@
autoRetrieveMmsPreference.setEnabled(false);
final Preference deliveryReportsPreference =
findPreference(getString(R.string.delivery_reports_pref_key));
- deliveryReportsPreference.setEnabled(false);
+ if (deliveryReportsPreference != null) {
+ deliveryReportsPreference.setEnabled(false);
+ }
+ }
+
+ if (advancedCategory.getPreferenceCount() == 0) {
+ getPreferenceScreen().removePreference(advancedCategory);
}
}
diff --git a/src/com/android/messaging/ui/appsettings/SettingsActivity.java b/src/com/android/messaging/ui/appsettings/SettingsActivity.java
index e8c05ec..dc16d0a 100644
--- a/src/com/android/messaging/ui/appsettings/SettingsActivity.java
+++ b/src/com/android/messaging/ui/appsettings/SettingsActivity.java
@@ -46,7 +46,7 @@
import java.util.List;
/**
- * Shows the "master" settings activity that contains two parts, one for application-wide settings
+ * Shows the "primary" settings activity that contains two parts, one for application-wide settings
* (dubbed "General settings"), and one or more for per-subscription settings (dubbed "Messaging
* settings" for single-SIM, and the actual SIM name for multi-SIM). Clicking on either item
* (e.g. "General settings") will open the detail settings activity (ApplicationSettingsActivity
diff --git a/src/com/android/messaging/ui/conversation/ComposeMessageView.java b/src/com/android/messaging/ui/conversation/ComposeMessageView.java
index 0f36e9a..c35927e 100644
--- a/src/com/android/messaging/ui/conversation/ComposeMessageView.java
+++ b/src/com/android/messaging/ui/conversation/ComposeMessageView.java
@@ -72,6 +72,7 @@
import com.android.messaging.util.UiUtils;
import com.android.messaging.util.UriUtil;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -683,7 +684,8 @@
final boolean hasWorkingDraft = hasMessageText || hasSubject ||
mBinding.getData().hasAttachments();
- final List<MessagePartData> attachments = draftMessageData.getReadOnlyAttachments();
+ final List<MessagePartData> attachments =
+ new ArrayList<MessagePartData>(draftMessageData.getReadOnlyAttachments());
if (draftMessageData.getIsMms()) { // MMS case
if (draftMessageData.hasAttachments()) {
if (hasAttachmentsChanged) {
diff --git a/src/com/android/messaging/ui/conversation/ConversationFragment.java b/src/com/android/messaging/ui/conversation/ConversationFragment.java
index 5c97c1c..6eb7089 100644
--- a/src/com/android/messaging/ui/conversation/ConversationFragment.java
+++ b/src/com/android/messaging/ui/conversation/ConversationFragment.java
@@ -51,6 +51,7 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.view.ActionMode;
import android.view.Display;
@@ -774,20 +775,27 @@
case R.id.action_call:
final String phoneNumber = mBinding.getData().getParticipantPhoneNumber();
Assert.notNull(phoneNumber);
- final View targetView = getActivity().findViewById(R.id.action_call);
- Point centerPoint;
- if (targetView != null) {
- final int screenLocation[] = new int[2];
- targetView.getLocationOnScreen(screenLocation);
- final int centerX = screenLocation[0] + targetView.getWidth() / 2;
- final int centerY = screenLocation[1] + targetView.getHeight() / 2;
- centerPoint = new Point(centerX, centerY);
+ // Can't make a call to emergency numbers using ACTION_CALL.
+ if (PhoneNumberUtils.isEmergencyNumber(phoneNumber)) {
+ UiUtils.showToast(R.string.disallow_emergency_call);
} else {
- // In the overflow menu, just use the center of the screen.
- final Display display = getActivity().getWindowManager().getDefaultDisplay();
- centerPoint = new Point(display.getWidth() / 2, display.getHeight() / 2);
+ final View targetView = getActivity().findViewById(R.id.action_call);
+ Point centerPoint;
+ if (targetView != null) {
+ final int screenLocation[] = new int[2];
+ targetView.getLocationOnScreen(screenLocation);
+ final int centerX = screenLocation[0] + targetView.getWidth() / 2;
+ final int centerY = screenLocation[1] + targetView.getHeight() / 2;
+ centerPoint = new Point(centerX, centerY);
+ } else {
+ // In the overflow menu, just use the center of the screen.
+ final Display display =
+ getActivity().getWindowManager().getDefaultDisplay();
+ centerPoint = new Point(display.getWidth() / 2, display.getHeight() / 2);
+ }
+ UIIntents.get()
+ .launchPhoneCallActivity(getActivity(), phoneNumber, centerPoint);
}
- UIIntents.get().launchPhoneCallActivity(getActivity(), phoneNumber, centerPoint);
return true;
case R.id.action_archive:
diff --git a/src/com/android/messaging/ui/conversation/ConversationMessageView.java b/src/com/android/messaging/ui/conversation/ConversationMessageView.java
index 20c986d..282e172 100644
--- a/src/com/android/messaging/ui/conversation/ConversationMessageView.java
+++ b/src/com/android/messaging/ui/conversation/ConversationMessageView.java
@@ -377,6 +377,7 @@
// FALL THROUGH HERE
case MessageData.BUGLE_STATUS_OUTGOING_COMPLETE:
+ case MessageData.BUGLE_STATUS_OUTGOING_DELIVERED:
case MessageData.BUGLE_STATUS_INCOMING_COMPLETE:
default:
if (!mData.getCanClusterWithNextMessage()) {
@@ -707,6 +708,7 @@
textBottomPadding = textBottomPaddingDefault;
textLeftPadding = messageTextLeftRightPadding;
textRightPadding = messageTextLeftRightPadding;
+ mMessageTextView.setTextIsSelectable(isSelected());
} else {
// Attachment(s) only
contentLeftPadding = incoming ? arrowWidth : 0;
@@ -732,6 +734,7 @@
textTopMargin = 0;
textTopPadding = textTopPaddingDefault;
textBottomPadding = textBottomPaddingDefault;
+ mMessageTextView.setTextIsSelectable(isSelected());
if (showArrow && incoming) {
textLeftPadding = messageTextLeftRightPadding + arrowWidth;
} else {
@@ -1029,6 +1032,11 @@
@Override
public boolean onLongClick(final View view) {
if (view == mMessageTextView) {
+ // Avoid trying to reselect the message
+ if (isSelected()) {
+ return false;
+ }
+
// Preemptively handle the long click event on message text so it's not handled by
// the link spans.
return performLongClick();
@@ -1194,7 +1202,7 @@
// This touch event is a long click, preemptively handle this touch event so that
// the link span won't get a onClicked() callback.
mIsLongClick = false;
- return true;
+ return false;
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
diff --git a/src/com/android/messaging/ui/conversation/LaunchConversationActivity.java b/src/com/android/messaging/ui/conversation/LaunchConversationActivity.java
index 8af9f75..5500ae8 100644
--- a/src/com/android/messaging/ui/conversation/LaunchConversationActivity.java
+++ b/src/com/android/messaging/ui/conversation/LaunchConversationActivity.java
@@ -61,7 +61,12 @@
final Intent intent = getIntent();
final String action = intent.getAction();
if (Intent.ACTION_SENDTO.equals(action) || Intent.ACTION_VIEW.equals(action)) {
- String[] recipients = UriUtil.parseRecipientsFromSmsMmsUri(intent.getData());
+ String[] recipients = null;
+ final String commaSeparatedRecipients =
+ UriUtil.parseRecipientsFromSmsMmsUri(intent.getData());
+ if (commaSeparatedRecipients != null) {
+ recipients = commaSeparatedRecipients.split(",");
+ }
final boolean haveAddress = !TextUtils.isEmpty(intent.getStringExtra(ADDRESS));
final boolean haveEmail = !TextUtils.isEmpty(intent.getStringExtra(Intent.EXTRA_EMAIL));
if (recipients == null && (haveAddress || haveEmail)) {
diff --git a/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java b/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java
index 412177e..9ac6e6b 100644
--- a/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java
+++ b/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java
@@ -81,6 +81,12 @@
public void onAttachFragment(final Fragment fragment) {
final Intent intent = getIntent();
final String action = intent.getAction();
+
+ String sharedSubject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
+ if (sharedSubject == null) {
+ sharedSubject = intent.getStringExtra(Intent.EXTRA_TITLE);
+ }
+
if (Intent.ACTION_SEND.equals(action)) {
final Uri contentUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (UriUtil.isFileUri(contentUri)) {
@@ -101,11 +107,14 @@
// Try to get text string from content uri.
sharedText = getTextStringFromContentUri(contentUri);
}
- mDraftMessage =
- sharedText != null ? MessageData.createSharedMessage(sharedText) : null;
+ if (sharedText != null) {
+ mDraftMessage = MessageData.createSharedMessage(sharedText, sharedSubject);
+ } else {
+ mDraftMessage = null;
+ }
} else if (PendingAttachmentData.isSupportedMediaType(contentType)) {
if (contentUri != null) {
- mDraftMessage = MessageData.createSharedMessage(null);
+ mDraftMessage = MessageData.createSharedMessage(null, sharedSubject);
addSharedPartToDraft(contentType, contentUri);
} else {
mDraftMessage = null;
@@ -149,7 +158,8 @@
}
if (strBuffer.length() > 0 || !uriMap.isEmpty()) {
- mDraftMessage = MessageData.createSharedMessage(strBuffer.toString());
+ mDraftMessage =
+ MessageData.createSharedMessage(strBuffer.toString(), sharedSubject);
for (final Map.Entry<Uri, String> e : uriMap.entrySet()) {
addSharedPartToDraft(e.getValue(), e.getKey());
}
diff --git a/src/com/android/messaging/ui/mediapicker/ContactMediaChooser.java b/src/com/android/messaging/ui/mediapicker/ContactMediaChooser.java
new file mode 100644
index 0000000..a81ed08
--- /dev/null
+++ b/src/com/android/messaging/ui/mediapicker/ContactMediaChooser.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 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.mediapicker;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.messaging.R;
+import com.android.messaging.datamodel.data.PendingAttachmentData;
+import com.android.messaging.ui.UIIntents;
+import com.android.messaging.util.ContactUtil;
+import com.android.messaging.util.ContentType;
+import com.android.messaging.util.SafeAsyncTask;
+
+/**
+ * Chooser which allows the user to select an existing contact from contacts apps on this device.
+ * Note that this chooser requires the Manifest.permission.READ_CONTACTS which is one of the miminum
+ * set of permissions for this app. Thus no case to request READ_CONTACTS permission on it actually.
+ */
+class ContactMediaChooser extends MediaChooser {
+ private View mEnabledView;
+ private View mMissingPermissionView;
+
+ ContactMediaChooser(final MediaPicker mediaPicker) {
+ super(mediaPicker);
+ }
+
+ @Override
+ public int getSupportedMediaTypes() {
+ return MediaPicker.MEDIA_TYPE_VCARD;
+ }
+
+ @Override
+ public int getIconResource() {
+ return R.drawable.ic_person_light;
+ }
+
+ @Override
+ public int getIconDescriptionResource() {
+ return R.string.mediapicker_contactChooserDescription;
+ }
+
+ @Override
+ int getActionBarTitleResId() {
+ return R.string.mediapicker_contact_title;
+ }
+
+ @Override
+ protected View createView(final ViewGroup container) {
+ final LayoutInflater inflater = getLayoutInflater();
+ final View view =
+ inflater.inflate(
+ R.layout.mediapicker_contact_chooser,
+ container /* root */,
+ false /* attachToRoot */);
+ mEnabledView = view.findViewById(R.id.mediapicker_enabled);
+ mMissingPermissionView = view.findViewById(R.id.missing_permission_view);
+ mEnabledView.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ // Launch an external picker to pick a contact as attachment.
+ UIIntents.get().launchContactCardPicker(mMediaPicker);
+ }
+ });
+ return view;
+ }
+
+ @Override
+ protected void setSelected(final boolean selected) {
+ super.setSelected(selected);
+ if (selected && !ContactUtil.hasReadContactsPermission()) {
+ mMediaPicker.requestPermissions(
+ new String[] {Manifest.permission.READ_CONTACTS},
+ MediaPicker.READ_CONTACT_PERMISSION_REQUEST_CODE);
+ }
+ }
+
+ @Override
+ protected void onRequestPermissionsResult(
+ final int requestCode, final String permissions[], final int[] grantResults) {
+ if (requestCode == MediaPicker.READ_CONTACT_PERMISSION_REQUEST_CODE) {
+ final boolean permissionGranted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ mEnabledView.setVisibility(permissionGranted ? View.VISIBLE : View.GONE);
+ mMissingPermissionView.setVisibility(permissionGranted ? View.GONE : View.VISIBLE);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == UIIntents.REQUEST_PICK_CONTACT_CARD
+ && resultCode == Activity.RESULT_OK) {
+ Uri contactUri = data.getData();
+ if (contactUri != null) {
+ String lookupKey = null;
+ try (final Cursor c = getContext().getContentResolver().query(
+ contactUri,
+ new String[] {Contacts.LOOKUP_KEY},
+ null,
+ null,
+ null)) {
+ if (c != null) {
+ c.moveToFirst();
+ lookupKey = c.getString(0);
+ }
+ }
+ final Uri vCardUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
+ if (vCardUri != null) {
+ SafeAsyncTask.executeOnThreadPool(new Runnable() {
+ @Override
+ public void run() {
+ final PendingAttachmentData pendingItem =
+ PendingAttachmentData.createPendingAttachmentData(
+ ContentType.TEXT_VCARD.toLowerCase(), vCardUri);
+ mMediaPicker.dispatchPendingItemAdded(pendingItem);
+ }
+ });
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java b/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java
index d6de128..c36467c 100644
--- a/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java
+++ b/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java
@@ -15,7 +15,6 @@
*/
package com.android.messaging.ui.mediapicker;
-import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.net.Uri;
@@ -79,30 +78,27 @@
* Must be called from the fragment/activity's onActivityResult().
*/
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
- if (requestCode == UIIntents.REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER
- && resultCode == Activity.RESULT_OK) {
- // Sometimes called after media item has been picked from the document picker.
- String url = data.getStringExtra(EXTRA_PHOTO_URL);
+ // Sometimes called after media item has been picked from the document picker.
+ String url = data.getStringExtra(EXTRA_PHOTO_URL);
+ if (url == null) {
+ // we're using the builtin photo picker which supplies the return
+ // url as it's "data"
+ url = data.getDataString();
if (url == null) {
- // we're using the builtin photo picker which supplies the return
- // url as it's "data"
- url = data.getDataString();
- if (url == null) {
- final Bundle extras = data.getExtras();
- if (extras != null) {
- final Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);
- if (uri != null) {
- url = uri.toString();
- }
+ final Bundle extras = data.getExtras();
+ if (extras != null) {
+ final Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);
+ if (uri != null) {
+ url = uri.toString();
}
}
}
+ }
- // Guard against null uri cases for when the activity returns a null/invalid intent.
- if (url != null) {
- final Uri uri = Uri.parse(url);
- prepareDocumentForAttachment(uri);
- }
+ // Guard against null uri cases for when the activity returns a null/invalid intent.
+ if (url != null) {
+ final Uri uri = Uri.parse(url);
+ prepareDocumentForAttachment(uri);
}
}
diff --git a/src/com/android/messaging/ui/mediapicker/GalleryGridItemView.java b/src/com/android/messaging/ui/mediapicker/GalleryGridItemView.java
index 48eaa5d..6cf509b 100644
--- a/src/com/android/messaging/ui/mediapicker/GalleryGridItemView.java
+++ b/src/com/android/messaging/ui/mediapicker/GalleryGridItemView.java
@@ -161,14 +161,9 @@
} else {
final String contentType = mData.getContentType();
if (ContentType.isAudioType(contentType)) {
- final Context context = getContext();
setBackgroundColor(
getResources().getColor(R.color.gallery_image_default_background));
- mIcon.setImageDrawable(
- context.getContentResolver()
- .getTypeInfo(contentType)
- .getIcon()
- .loadDrawable(context));
+ mIcon.setImageResource(R.drawable.ic_music);
mIcon.setColorFilter(
ConversationDrawables.get().getConversationThemeColor(),
PorterDuff.Mode.SRC_IN);
diff --git a/src/com/android/messaging/ui/mediapicker/GalleryMediaChooser.java b/src/com/android/messaging/ui/mediapicker/GalleryMediaChooser.java
index c9b544d..6192c4f 100644
--- a/src/com/android/messaging/ui/mediapicker/GalleryMediaChooser.java
+++ b/src/com/android/messaging/ui/mediapicker/GalleryMediaChooser.java
@@ -17,6 +17,8 @@
package com.android.messaging.ui.mediapicker;
import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.MatrixCursor;
@@ -35,6 +37,9 @@
import com.android.messaging.datamodel.data.MediaPickerData;
import com.android.messaging.datamodel.data.MessagePartData;
import com.android.messaging.datamodel.data.MediaPickerData.MediaPickerDataListener;
+import com.android.messaging.datamodel.data.PendingAttachmentData;
+import com.android.messaging.ui.UIIntents;
+import com.android.messaging.ui.mediapicker.DocumentImagePicker.SelectionListener;
import com.android.messaging.util.Assert;
import com.android.messaging.util.OsUtil;
@@ -47,9 +52,21 @@
private GalleryGridView mGalleryGridView;
private View mMissingPermissionView;
+ /** Handles picking a media from the document picker. */
+ private DocumentImagePicker mDocumentImagePicker;
+
GalleryMediaChooser(final MediaPicker mediaPicker) {
super(mediaPicker);
mAdapter = new GalleryGridAdapter(Factory.get().getApplicationContext(), null);
+ mDocumentImagePicker = new DocumentImagePicker(mMediaPicker,
+ new SelectionListener() {
+ @Override
+ public void onDocumentSelected(final PendingAttachmentData data) {
+ if (mBindingRef.isBound()) {
+ mMediaPicker.dispatchPendingItemAdded(data);
+ }
+ }
+ });
}
@Override
@@ -148,7 +165,8 @@
@Override
public void onDocumentPickerItemClicked() {
- mMediaPicker.launchDocumentPicker();
+ // Launch an external picker to pick item from document picker as attachment.
+ mDocumentImagePicker.launchPicker();
}
@Override
@@ -230,4 +248,13 @@
mGalleryGridView.setVisibility(granted ? View.VISIBLE : View.GONE);
mMissingPermissionView.setVisibility(granted ? View.GONE : View.VISIBLE);
}
+
+ @Override
+ protected void onActivityResult(
+ final int requestCode, final int resultCode, final Intent data) {
+ if (requestCode == UIIntents.REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER
+ && resultCode == Activity.RESULT_OK) {
+ mDocumentImagePicker.onActivityResult(requestCode, resultCode, data);
+ }
+ }
}
diff --git a/src/com/android/messaging/ui/mediapicker/MediaChooser.java b/src/com/android/messaging/ui/mediapicker/MediaChooser.java
index ef4067c..0549e5b 100644
--- a/src/com/android/messaging/ui/mediapicker/MediaChooser.java
+++ b/src/com/android/messaging/ui/mediapicker/MediaChooser.java
@@ -18,6 +18,7 @@
import android.app.FragmentManager;
import android.content.Context;
+import android.content.Intent;
import androidx.appcompat.app.ActionBar;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -203,6 +204,9 @@
public void stopTouchHandling() {
}
+ protected void onActivityResult(
+ final int requestCode, final int resultCode, final Intent data) {}
+
@Override
public int getConversationSelfSubId() {
return mMediaPicker.getConversationSelfSubId();
diff --git a/src/com/android/messaging/ui/mediapicker/MediaPicker.java b/src/com/android/messaging/ui/mediapicker/MediaPicker.java
index b8fce8f..c7faa33 100644
--- a/src/com/android/messaging/ui/mediapicker/MediaPicker.java
+++ b/src/com/android/messaging/ui/mediapicker/MediaPicker.java
@@ -16,7 +16,6 @@
package com.android.messaging.ui.mediapicker;
-
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
@@ -48,7 +47,6 @@
import com.android.messaging.datamodel.data.DraftMessageData.DraftMessageSubscriptionDataProvider;
import com.android.messaging.ui.BugleActionBarActivity;
import com.android.messaging.ui.FixedViewPagerAdapter;
-import com.android.messaging.ui.mediapicker.DocumentImagePicker.SelectionListener;
import com.android.messaging.util.AccessibilityUtil;
import com.android.messaging.util.Assert;
import com.android.messaging.util.UiUtils;
@@ -159,9 +157,6 @@
@VisibleForTesting
final Binding<MediaPickerData> mBinding = BindingBase.createBinding(this);
- /** Handles picking a media from the document picker. */
- private DocumentImagePicker mDocumentImagePicker;
-
/** Provides subscription-related data to access per-subscription configurations. */
private DraftMessageSubscriptionDataProvider mSubscriptionDataProvider;
@@ -179,6 +174,7 @@
new CameraMediaChooser(this),
new GalleryMediaChooser(this),
new AudioMediaChooser(this),
+ new ContactMediaChooser(this),
};
mOpen = false;
@@ -203,15 +199,6 @@
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding.getData().init(getLoaderManager());
- mDocumentImagePicker = new DocumentImagePicker(this,
- new SelectionListener() {
- @Override
- public void onDocumentSelected(final PendingAttachmentData data) {
- if (mBinding.isBound()) {
- dispatchPendingItemAdded(data);
- }
- }
- });
}
@Override
@@ -295,7 +282,7 @@
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- mDocumentImagePicker.onActivityResult(requestCode, resultCode, data);
+ mSelectedChooser.onActivityResult(requestCode, resultCode, data);
}
@Override
@@ -710,13 +697,6 @@
mPagerAdapter.resetState();
}
- /**
- * Launch an external picker to pick item from document picker as attachment.
- */
- public void launchDocumentPicker() {
- mDocumentImagePicker.launchPicker();
- }
-
public ImmutableBindingRef<MediaPickerData> getMediaPickerDataBinding() {
return BindingBase.createBindingReference(mBinding);
}
@@ -725,6 +705,7 @@
protected static final int LOCATION_PERMISSION_REQUEST_CODE = 2;
protected static final int RECORD_AUDIO_PERMISSION_REQUEST_CODE = 3;
protected static final int GALLERY_PERMISSION_REQUEST_CODE = 4;
+ protected static final int READ_CONTACT_PERMISSION_REQUEST_CODE = 5;
@Override
public void onRequestPermissionsResult(
diff --git a/src/com/android/messaging/util/BuglePrefsKeys.java b/src/com/android/messaging/util/BuglePrefsKeys.java
index ae409bc..a30b5a3 100644
--- a/src/com/android/messaging/util/BuglePrefsKeys.java
+++ b/src/com/android/messaging/util/BuglePrefsKeys.java
@@ -66,6 +66,6 @@
* The attempt number when retrying ProcessPendingMessagesAction
*/
public static final String PROCESS_PENDING_MESSAGES_RETRY_COUNT
- = "process_pending_retry";
+ = BuglePrefs.SHARED_PREFERENCES_PER_SUBSCRIPTION_PREFIX + "process_pending_retry";
}
diff --git a/src/com/android/messaging/util/ConnectivityUtil.java b/src/com/android/messaging/util/ConnectivityUtil.java
index 49f6e0a..a7467e4 100644
--- a/src/com/android/messaging/util/ConnectivityUtil.java
+++ b/src/com/android/messaging/util/ConnectivityUtil.java
@@ -16,40 +16,44 @@
package com.android.messaging.util;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
+import com.android.messaging.datamodel.data.ParticipantData;
+
+/**
+ * ConnectivityUtil listens to the network service state changes.
+ *
+ * On N and beyond, This class instance can be created via ConnectivityUtil(context, subId), use
+ * ConnectivityUtil(context) for others.
+ *
+ * Note that TelephonyManager has createForSubscriptionId() for a specific subId from N but listen()
+ * does not use the subId on the manager, and uses the default subId on PhoneStateListener. From O,
+ * the manager uses its' own subId in listen().
+ */
public class ConnectivityUtil {
// Assume not connected until informed differently
private volatile int mCurrentServiceState = ServiceState.STATE_POWER_OFF;
private final TelephonyManager mTelephonyManager;
- private final Context mContext;
- private final ConnectivityBroadcastReceiver mReceiver;
- private final ConnectivityManager mConnMgr;
private ConnectivityListener mListener;
- private final IntentFilter mIntentFilter;
public interface ConnectivityListener {
- public void onConnectivityStateChanged(final Context context, final Intent intent);
- public void onPhoneStateChanged(final Context context, int serviceState);
+ public void onPhoneStateChanged(int serviceState);
}
public ConnectivityUtil(final Context context) {
- mContext = context;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- mReceiver = new ConnectivityBroadcastReceiver();
- mIntentFilter = new IntentFilter();
- mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ }
+
+ public ConnectivityUtil(final Context context, final int subId) {
+ Assert.isTrue(OsUtil.isAtLeastN());
+ mTelephonyManager =
+ ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+ .createForSubscriptionId(subId);
}
public int getCurrentServiceState() {
@@ -75,14 +79,7 @@
private void onPhoneStateChanged(final int serviceState) {
final ConnectivityListener listener = mListener;
if (listener != null) {
- listener.onPhoneStateChanged(mContext, serviceState);
- }
- }
-
- private void onConnectivityChanged(final Context context, final Intent intent) {
- final ConnectivityListener listener = mListener;
- if (listener != null) {
- listener.onConnectivityStateChanged(context, intent);
+ listener.onPhoneStateChanged(serviceState);
}
}
@@ -95,9 +92,6 @@
mTelephonyManager.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_SERVICE_STATE);
}
- if (mConnMgr != null) {
- mContext.registerReceiver(mReceiver, mIntentFilter);
- }
}
mListener = listener;
}
@@ -108,140 +102,7 @@
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
mCurrentServiceState = ServiceState.STATE_POWER_OFF;
}
- if (mConnMgr != null) {
- mContext.unregisterReceiver(mReceiver);
- }
}
mListener = null;
}
-
- /**
- * Connectivity change broadcast receiver. This gets the network connectivity updates.
- * In case we don't get the active connectivity when we first acquire the network,
- * this receiver will notify us when it is connected, so to unblock the waiting thread
- * which is sending the message.
- */
- public class ConnectivityBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(final Context context, final Intent intent) {
- if (!intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- return;
- }
-
- onConnectivityChanged(context, intent);
- }
- }
-
- private int mSignalLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
- // We use a separate instance than mPhoneStateListener because the lifetimes are different.
- private final PhoneStateListener mSignalStrengthListener = new PhoneStateListener() {
- @Override
- public void onSignalStrengthsChanged(final SignalStrength signalStrength) {
- mSignalLevel = getLevel(signalStrength);
- }
- };
-
- public void registerForSignalStrength() {
- mTelephonyManager.listen(
- mSignalStrengthListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
- }
-
- public void unregisterForSignalStrength() {
- mTelephonyManager.listen(mSignalStrengthListener, PhoneStateListener.LISTEN_NONE);
- }
-
- /**
- * @param subId This is ignored because TelephonyManager does not support it.
- * @return Signal strength as level 0..4
- */
- public int getSignalLevel(final int subId) {
- return mSignalLevel;
- }
-
- private static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
- private static final int SIGNAL_STRENGTH_POOR = 1;
- private static final int SIGNAL_STRENGTH_MODERATE = 2;
- private static final int SIGNAL_STRENGTH_GOOD = 3;
- private static final int SIGNAL_STRENGTH_GREAT = 4;
-
- private static final int GSM_SIGNAL_STRENGTH_GREAT = 12;
- private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
- private static final int GSM_SIGNAL_STRENGTH_MODERATE = 8;
-
- private static int getLevel(final SignalStrength signalStrength) {
- if (signalStrength.isGsm()) {
- // From frameworks/base/telephony/java/android/telephony/CellSignalStrengthGsm.java
-
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- final int asu = signalStrength.getGsmSignalStrength();
- if (asu <= 2 || asu == 99) return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- else if (asu >= GSM_SIGNAL_STRENGTH_GREAT) return SIGNAL_STRENGTH_GREAT;
- else if (asu >= GSM_SIGNAL_STRENGTH_GOOD) return SIGNAL_STRENGTH_GOOD;
- else if (asu >= GSM_SIGNAL_STRENGTH_MODERATE) return SIGNAL_STRENGTH_MODERATE;
- else return SIGNAL_STRENGTH_POOR;
- } else {
- // From frameworks/base/telephony/java/android/telephony/CellSignalStrengthCdma.java
-
- final int cdmaLevel = getCdmaLevel(signalStrength);
- final int evdoLevel = getEvdoLevel(signalStrength);
- if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- /* We don't know evdo, use cdma */
- return getCdmaLevel(signalStrength);
- } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- /* We don't know cdma, use evdo */
- return getEvdoLevel(signalStrength);
- } else {
- /* We know both, use the lowest level */
- return cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
- }
- }
- }
-
- /**
- * Get cdma as level 0..4
- */
- private static int getCdmaLevel(final SignalStrength signalStrength) {
- final int cdmaDbm = signalStrength.getCdmaDbm();
- final int cdmaEcio = signalStrength.getCdmaEcio();
- int levelDbm;
- int levelEcio;
- if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
- else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
- else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
- else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR;
- else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- // Ec/Io are in dB*10
- if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
- else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
- else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
- else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR;
- else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- final int level = (levelDbm < levelEcio) ? levelDbm : levelEcio;
- return level;
- }
- /**
- * Get Evdo as level 0..4
- */
- private static int getEvdoLevel(final SignalStrength signalStrength) {
- final int evdoDbm = signalStrength.getEvdoDbm();
- final int evdoSnr = signalStrength.getEvdoSnr();
- int levelEvdoDbm;
- int levelEvdoSnr;
- if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
- else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
- else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
- else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
- else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
- else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
- else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
- else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR;
- else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- final int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
- return level;
- }
}
diff --git a/src/com/android/messaging/util/DebugUtils.java b/src/com/android/messaging/util/DebugUtils.java
index 0d689fe..6e33f48 100644
--- a/src/com/android/messaging/util/DebugUtils.java
+++ b/src/com/android/messaging/util/DebugUtils.java
@@ -302,11 +302,10 @@
final int length = dis.readInt();
final byte[] pdu = new byte[length];
dis.read(pdu, 0, length);
- if (format == null) {
- messagesTemp[i] = SmsMessage.createFromPdu(pdu);
- } else {
- messagesTemp[i] = SmsMessage.createFromPdu(pdu, format);
- }
+ messagesTemp[i] =
+ OsUtil.isAtLeastM()
+ ? SmsMessage.createFromPdu(pdu, format)
+ : SmsMessage.createFromPdu(pdu);
}
messages = messagesTemp;
} catch (final FileNotFoundException e) {
diff --git a/src/com/android/messaging/util/FileUtil.java b/src/com/android/messaging/util/FileUtil.java
index 7d59aa7..71fbb4b 100644
--- a/src/com/android/messaging/util/FileUtil.java
+++ b/src/com/android/messaging/util/FileUtil.java
@@ -117,15 +117,11 @@
}
}
- private static boolean isFileUri(final Uri uri) {
- return TextUtils.equals(uri.getScheme(), ContentResolver.SCHEME_FILE);
- }
-
// Checks if the file is in /data, and don't allow any app to send personal information.
// We're told it's possible to create world readable hardlinks to other apps private data
// so we ban all /data file uris.
public static boolean isInPrivateDir(Uri uri) {
- if (!isFileUri(uri)) {
+ if (!UriUtil.isFileUri(uri)) {
return false;
}
final File file = new File(uri.getPath());
diff --git a/src/com/android/messaging/util/ImageUtils.java b/src/com/android/messaging/util/ImageUtils.java
index a228417..e541d69 100644
--- a/src/com/android/messaging/util/ImageUtils.java
+++ b/src/com/android/messaging/util/ImageUtils.java
@@ -480,7 +480,7 @@
if (MediaScratchFileProvider.isMediaScratchSpaceUri(mUri)) {
inputFilePath = MediaScratchFileProvider.getFileFromUri(mUri).getAbsolutePath();
} else {
- if (!TextUtils.equals(mUri.getScheme(), ContentResolver.SCHEME_FILE)) {
+ if (!UriUtil.isFileUri(mUri)) {
Assert.fail("Expected a GIF file uri, but actual uri = " + mUri.toString());
}
inputFilePath = mUri.getPath();
diff --git a/src/com/android/messaging/util/MediaMetadataRetrieverWrapper.java b/src/com/android/messaging/util/MediaMetadataRetrieverWrapper.java
index b1078d1..1a93e9d 100644
--- a/src/com/android/messaging/util/MediaMetadataRetrieverWrapper.java
+++ b/src/com/android/messaging/util/MediaMetadataRetrieverWrapper.java
@@ -74,7 +74,7 @@
public void release() {
try {
mRetriever.release();
- } catch (RuntimeException e) {
+ } catch (RuntimeException | IOException e) {
LogUtil.e(LogUtil.BUGLE_TAG, "MediaMetadataRetriever.release failed", e);
}
}
diff --git a/src/com/android/messaging/util/PhoneUtils.java b/src/com/android/messaging/util/PhoneUtils.java
index 671c65e..08889b8 100644
--- a/src/com/android/messaging/util/PhoneUtils.java
+++ b/src/com/android/messaging/util/PhoneUtils.java
@@ -26,7 +26,6 @@
import android.net.ConnectivityManager;
import android.provider.Settings;
import android.provider.Telephony;
-import androidx.collection.ArrayMap;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import android.telephony.SubscriptionInfo;
@@ -34,14 +33,18 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import androidx.collection.ArrayMap;
+import androidx.core.os.BuildCompat;
+
import com.android.messaging.Factory;
import com.android.messaging.R;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.sms.MmsSmsUtils;
+
import com.google.i18n.phonenumbers.NumberParseException;
-import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -369,6 +372,9 @@
@Override
@SuppressWarnings("deprecation")
public boolean isDataRoamingEnabled() {
+ if (BuildCompat.isAtLeastT()) {
+ return mTelephonyManager.isDataRoamingEnabled();
+ }
boolean dataRoamingEnabled = false;
final ContentResolver cr = mContext.getContentResolver();
if (OsUtil.isAtLeastJB_MR1()) {
diff --git a/src/com/android/messaging/util/Trace.java b/src/com/android/messaging/util/Trace.java
index da1e87c..69910f7 100644
--- a/src/com/android/messaging/util/Trace.java
+++ b/src/com/android/messaging/util/Trace.java
@@ -101,7 +101,7 @@
}
/**
- * Dummy class that we use if we aren't really tracing.
+ * Placeholder class that we use if we aren't really tracing.
*/
private static final class TraceShim extends AbstractTrace {
@Override
diff --git a/src/com/android/messaging/util/UriUtil.java b/src/com/android/messaging/util/UriUtil.java
index ceff50c..f92155f 100644
--- a/src/com/android/messaging/util/UriUtil.java
+++ b/src/com/android/messaging/util/UriUtil.java
@@ -49,6 +49,8 @@
private static final String SCHEME_MMSTO = "smsto";
public static final HashSet<String> SMS_MMS_SCHEMES = new HashSet<String>(
Arrays.asList(SCHEME_SMS, SCHEME_MMS, SCHEME_SMSTO, SCHEME_MMSTO));
+ private static final String SCHEME_HTTP = "http";
+ private static final String SCHEME_HTTPS = "https";
public static final String SCHEME_BUGLE = "bugle";
public static final HashSet<String> SUPPORTED_SCHEME = new HashSet<String>(
@@ -94,8 +96,11 @@
return TextUtils.equals(scheme, ContentResolver.SCHEME_ANDROID_RESOURCE);
}
+ /** Returns whether the given Uri is a file. */
public static boolean isFileUri(final Uri uri) {
- return uri != null && TextUtils.equals(uri.getScheme(), ContentResolver.SCHEME_FILE);
+ return uri != null &&
+ uri.getScheme() != null &&
+ uri.getScheme().trim().toLowerCase().contains(ContentResolver.SCHEME_FILE);
}
/**
@@ -212,9 +217,10 @@
inputStream = context.getContentResolver().openInputStream(sourceUri);
} else {
// The content is remote. Download it.
- final URL url = new URL(sourceUri.toString());
- final URLConnection ucon = url.openConnection();
- inputStream = new BufferedInputStream(ucon.getInputStream());
+ inputStream = getInputStreamFromRemoteUri(sourceUri);
+ if (inputStream == null) {
+ return null;
+ }
}
return persistContentToScratchSpace(inputStream);
} catch (final Exception ex) {
@@ -231,6 +237,23 @@
}
}
+ @DoesNotRunOnMainThread
+ private static InputStream getInputStreamFromRemoteUri(final Uri sourceUri)
+ throws IOException {
+ if (isRemoteUri(sourceUri)) {
+ final URL url = new URL(sourceUri.toString());
+ final URLConnection ucon = url.openConnection();
+ return new BufferedInputStream(ucon.getInputStream());
+ } else {
+ return null;
+ }
+ }
+
+ private static boolean isRemoteUri(final Uri sourceUri) {
+ return sourceUri.getScheme().equals(SCHEME_HTTP)
+ || sourceUri.getScheme().equals(SCHEME_HTTPS);
+ }
+
/**
* Persist a piece of content from the given input stream, byte by byte to the specified
* directory.
@@ -269,9 +292,10 @@
inputStream = context.getContentResolver().openInputStream(sourceUri);
} else {
// The content is remote. Download it.
- final URL url = new URL(sourceUri.toString());
- final URLConnection ucon = url.openConnection();
- inputStream = new BufferedInputStream(ucon.getInputStream());
+ inputStream = getInputStreamFromRemoteUri(sourceUri);
+ if (inputStream == null) {
+ return null;
+ }
}
return persistContent(inputStream, outputDir, contentType);
} catch (final Exception ex) {
@@ -324,14 +348,13 @@
}
/**
- * Extract recipient destinations from Uri of form
- * SCHEME:destionation[,destination]?otherstuff
+ * Extract recipient destinations from Uri of form SCHEME:destination[,destination]?otherstuff
* where SCHEME is one of the supported sms/mms schemes.
*
* @param uri sms/mms uri
- * @return recipient destinations or null
+ * @return a comma-separated list of recipient destinations or null.
*/
- public static String[] parseRecipientsFromSmsMmsUri(final Uri uri) {
+ public static String parseRecipientsFromSmsMmsUri(final Uri uri) {
if (!isSmsMmsUri(uri)) {
return null;
}
@@ -341,7 +364,7 @@
}
// replaceUnicodeDigits will replace digits typed in other languages (i.e. Egyptian) with
// the usual ascii equivalents.
- return TextUtil.replaceUnicodeDigits(parts[0]).replace(';', ',').split(",");
+ return TextUtil.replaceUnicodeDigits(parts[0]).replace(';', ',');
}
/**
diff --git a/src/com/android/messaging/widget/WidgetConversationService.java b/src/com/android/messaging/widget/WidgetConversationService.java
index 4fd3934..5b8587b 100644
--- a/src/com/android/messaging/widget/WidgetConversationService.java
+++ b/src/com/android/messaging/widget/WidgetConversationService.java
@@ -330,6 +330,7 @@
// FALL THROUGH HERE
case MessageData.BUGLE_STATUS_OUTGOING_COMPLETE:
+ case MessageData.BUGLE_STATUS_OUTGOING_DELIVERED:
case MessageData.BUGLE_STATUS_INCOMING_COMPLETE:
default:
if (!message.getCanClusterWithNextMessage()) {
diff --git a/tests/Android.mk b/tests/Android.mk
index aaff57a..0c93ea8 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -21,6 +21,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := messagingtests
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_INSTRUMENTATION_FOR := messaging
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index 601bc8e..cd66c48 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
<option name="test-file-name" value="messaging.apk" />
</target_preparer>
- <test class="com.android.tradefed.testtype.InstrumentationTest" >
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.messaging.test" />
<option name="runner" value="android.test.InstrumentationTestRunner" />
</test>
diff --git a/tests/src/com/android/messaging/FakeFactory.java b/tests/src/com/android/messaging/FakeFactory.java
index 41ede77..4c7c9de 100644
--- a/tests/src/com/android/messaging/FakeFactory.java
+++ b/tests/src/com/android/messaging/FakeFactory.java
@@ -46,6 +46,7 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.ArrayList;
import java.util.List;
public class FakeFactory extends Factory {
@@ -121,7 +122,8 @@
@Override
public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
- return null;
+ // Return empty list
+ return new ArrayList<>();
}
@Override
diff --git a/tests/src/com/android/messaging/datamodel/DataModelTest.java b/tests/src/com/android/messaging/datamodel/DataModelTest.java
index 71723a4..0a1bccd 100644
--- a/tests/src/com/android/messaging/datamodel/DataModelTest.java
+++ b/tests/src/com/android/messaging/datamodel/DataModelTest.java
@@ -35,10 +35,10 @@
@Override
protected void setUp() throws Exception {
- super.setUp();
- dataModel = new DataModelImpl(getTestContext());
- FakeFactory.register(mContext)
- .withDataModel(dataModel);
+ super.setUp();
+ FakeFactory factory = FakeFactory.register(mContext);
+ dataModel = new DataModelImpl(getTestContext());
+ factory.withDataModel(dataModel);
}
@SmallTest
diff --git a/tests/src/com/android/messaging/datamodel/FakeDataModel.java b/tests/src/com/android/messaging/datamodel/FakeDataModel.java
index 5e80eab..1daaa20 100644
--- a/tests/src/com/android/messaging/datamodel/FakeDataModel.java
+++ b/tests/src/com/android/messaging/datamodel/FakeDataModel.java
@@ -48,7 +48,6 @@
import com.android.messaging.datamodel.data.SubscriptionListData;
import com.android.messaging.datamodel.data.TestDataFactory;
import com.android.messaging.datamodel.data.VCardContactItemData;
-import com.android.messaging.util.ConnectivityUtil;
public class FakeDataModel extends DataModel {
private BackgroundWorker mWorker;
@@ -58,7 +57,6 @@
private ContactPickerData mContactPickerData;
private MediaPickerData mMediaPickerData;
private PeopleAndOptionsData mPeopleAndOptionsData;
- private ConnectivityUtil mConnectivityUtil;
private SyncManager mSyncManager;
private SettingsData mSettingsData;
private DraftMessageData mDraftMessageData;
@@ -102,11 +100,6 @@
return this;
}
- public FakeDataModel withConnectivityUtil(final ConnectivityUtil connectivityUtil) {
- mConnectivityUtil = connectivityUtil;
- return this;
- }
-
public FakeDataModel withSyncManager(final SyncManager syncManager) {
mSyncManager = syncManager;
return this;
@@ -220,11 +213,6 @@
}
@Override
- public ConnectivityUtil getConnectivityUtil() {
- return mConnectivityUtil;
- }
-
- @Override
public SyncManager getSyncManager() {
return mSyncManager;
}
diff --git a/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java b/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java
index 97e0f10..4d1ad5d 100644
--- a/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java
+++ b/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java
@@ -35,7 +35,6 @@
import com.android.messaging.datamodel.action.ActionMonitor.ActionExecutedListener;
import com.android.messaging.datamodel.action.ActionTestHelpers.ResultTracker;
import com.android.messaging.datamodel.action.ActionTestHelpers.StubBackgroundWorker;
-import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
import com.android.messaging.datamodel.action.ActionTestHelpers.StubLoader;
import java.util.ArrayList;
@@ -291,8 +290,7 @@
FakeFactory.registerWithFakeContext(getContext(), mContext)
.withDataModel(new FakeDataModel(mContext)
.withBackgroundWorkerForActionService(mWorker)
- .withActionService(new ActionService())
- .withConnectivityUtil(new StubConnectivityUtil(mContext)));
+ .withActionService(new ActionService()));
mLoader = new StubLoader();
setContext(Factory.get().getApplicationContext());
diff --git a/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java b/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java
index 6f66fa9..8daafb8 100644
--- a/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java
+++ b/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java
@@ -35,7 +35,6 @@
import com.android.messaging.datamodel.action.ActionMonitor.ActionStateChangedListener;
import com.android.messaging.datamodel.action.ActionTestHelpers.ResultTracker;
import com.android.messaging.datamodel.action.ActionTestHelpers.StubBackgroundWorker;
-import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
import com.android.messaging.datamodel.action.ActionTestHelpers.StubLoader;
import java.util.ArrayList;
@@ -81,7 +80,7 @@
}
/**
- * For a dummy action verify that the service intent is constructed and queued correctly and
+ * For a chat action verify that the service intent is constructed and queued correctly and
* that when that intent is processed it actually executes the action.
*/
public void testChatServiceCreatesIntentAndExecutesAction() {
@@ -145,8 +144,7 @@
FakeFactory.registerWithFakeContext(getContext(),mContext)
.withDataModel(new FakeDataModel(mContext)
.withBackgroundWorkerForActionService(mWorker)
- .withActionService(new ActionService())
- .withConnectivityUtil(new StubConnectivityUtil(mContext)));
+ .withActionService(new ActionService()));
mStates = new ArrayList<Integer>();
setContext(Factory.get().getApplicationContext());
diff --git a/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java b/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java
index d72a0f9..00aa267 100644
--- a/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java
+++ b/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java
@@ -21,8 +21,6 @@
import android.net.Uri;
import android.os.Bundle;
-import com.android.messaging.util.ConnectivityUtil;
-
import java.util.ArrayList;
import java.util.List;
@@ -174,18 +172,4 @@
}
}
}
-
- public static class StubConnectivityUtil extends ConnectivityUtil {
- public StubConnectivityUtil(final Context context) {
- super(context);
- }
-
- @Override
- public void registerForSignalStrength() {
- }
-
- @Override
- public void unregisterForSignalStrength() {
- }
- }
}
diff --git a/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java b/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java
index 0405c90..e977aa7 100644
--- a/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java
+++ b/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java
@@ -36,7 +36,6 @@
import com.android.messaging.datamodel.MessagingContentProvider;
import com.android.messaging.datamodel.action.ActionTestHelpers.StubActionService;
import com.android.messaging.datamodel.action.ActionTestHelpers.StubActionService.StubActionServiceCallLog;
-import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
import com.android.messaging.datamodel.action.ReadDraftDataAction.ReadDraftDataActionListener;
import com.android.messaging.datamodel.data.MessageData;
import com.android.messaging.datamodel.data.MessagePartData;
@@ -473,8 +472,7 @@
mService = new StubActionService();
final FakeDataModel fakeDataModel = new FakeDataModel(context)
- .withActionService(mService)
- .withConnectivityUtil(new StubConnectivityUtil(context));
+ .withActionService(mService);
FakeFactory.registerWithFakeContext(getTestContext(), context)
.withDataModel(fakeDataModel);
diff --git a/tests/src/com/android/messaging/ui/contact/ContactPickerFragmentTest.java b/tests/src/com/android/messaging/ui/contact/ContactPickerFragmentTest.java
index 1997a4d..47b65f1 100644
--- a/tests/src/com/android/messaging/ui/contact/ContactPickerFragmentTest.java
+++ b/tests/src/com/android/messaging/ui/contact/ContactPickerFragmentTest.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.database.Cursor;
+import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import android.test.suitebuilder.annotation.LargeTest;
import android.view.View;
diff --git a/tests/src/com/android/messaging/ui/conversation/ConversationFragmentTest.java b/tests/src/com/android/messaging/ui/conversation/ConversationFragmentTest.java
index 797a2f1..658a1be 100644
--- a/tests/src/com/android/messaging/ui/conversation/ConversationFragmentTest.java
+++ b/tests/src/com/android/messaging/ui/conversation/ConversationFragmentTest.java
@@ -20,6 +20,7 @@
import android.app.Fragment;
import android.database.Cursor;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
import android.test.suitebuilder.annotation.LargeTest;
import com.android.messaging.FakeFactory;
diff --git a/tests/src/com/android/messaging/ui/mediapicker/MediaPickerTest.java b/tests/src/com/android/messaging/ui/mediapicker/MediaPickerTest.java
index eaf9338..4e7c2d2 100644
--- a/tests/src/com/android/messaging/ui/mediapicker/MediaPickerTest.java
+++ b/tests/src/com/android/messaging/ui/mediapicker/MediaPickerTest.java
@@ -103,7 +103,7 @@
final View view = mediaPicker.getView();
assertNotNull(view);
final ViewGroup tabStrip = (ViewGroup) view.findViewById(R.id.mediapicker_tabstrip);
- assertEquals(tabStrip.getChildCount(), 3);
+ assertEquals(tabStrip.getChildCount(), 4);
for (int i = 0; i < tabStrip.getChildCount(); i++) {
final ImageButton tabButton = (ImageButton) tabStrip.getChildAt(i);
assertEquals(View.VISIBLE, tabButton.getVisibility());
@@ -120,7 +120,7 @@
final View view = mediaPicker.getView();
assertNotNull(view);
final ViewGroup tabStrip = (ViewGroup) view.findViewById(R.id.mediapicker_tabstrip);
- assertEquals(tabStrip.getChildCount(), 3);
+ assertEquals(tabStrip.getChildCount(), 4);
for (int i = 0; i < tabStrip.getChildCount(); i++) {
final ImageButton tabButton = (ImageButton) tabStrip.getChildAt(i);
assertEquals(i == 0, tabButton.isSelected());