Merge tag 'LA.UM.5.6.r1-03600-89xx.0' of https://source.codeaurora.org/quic/la/platform/packages/apps/Dialer into HEAD
"LA.UM.5.6.r1-03600-89xx.0"
Change-Id: If44100b3cd8e1278463cb0c210ad705a80bb6154
Signed-off-by: Jackeagle <jackeagle102@gmail.com>
diff --git a/Android.mk b/Android.mk
index d2863fb..9dddb9e 100755
--- a/Android.mk
+++ b/Android.mk
@@ -38,7 +38,8 @@
LOCAL_JAVA_LIBRARIES := telephony-common \
telephony-ext \
- ims-common
+ ims-common \
+ rcscommon
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 02d8723..af291ac 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -69,7 +69,9 @@
start requests, even if they happen immediately after the user
presses home. -->
<uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+ <!-- For enrich call -->
+ <uses-permission android:name="com.qualcomm.qti.internal.permission.ACCESS_ENRICHED_CALL" />
<application
android:name="DialerApplication"
@@ -80,6 +82,8 @@
android:backupAgent='com.android.dialer.DialerBackupAgent'
android:usesCleartextTraffic="false">
+ <uses-library android:name="org.codeaurora.rcscommon" android:required="false" />
+
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAIBXgtCEKQ6W0PXVnW-ZVia2KmlV2AxsTw3GjAeQ" />
diff --git a/InCallUI/res/anim/pulse.xml b/InCallUI/res/anim/pulse.xml
new file mode 100644
index 0000000..b4415b3
--- /dev/null
+++ b/InCallUI/res/anim/pulse.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true"
+ android:fillEnabled="true">
+
+ <scale xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="1000"
+ android:fromXScale="0.8"
+ android:fromYScale="0.8"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:repeatCount="-1"
+ android:repeatMode="reverse"
+ android:toXScale="1"
+ android:toYScale="1" />
+</set>
diff --git a/InCallUI/res/anim/scale_in.xml b/InCallUI/res/anim/scale_in.xml
new file mode 100644
index 0000000..04c8948
--- /dev/null
+++ b/InCallUI/res/anim/scale_in.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillEnabled="true"
+ android:fillAfter="true">
+ <scale
+
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromXScale="1.0"
+ android:toXScale="0.0"
+ android:fromYScale="1.0"
+ android:toYScale="0.0"
+ android:fillAfter="false"
+ />
+
+</set>
\ No newline at end of file
diff --git a/InCallUI/res/anim/scale_in_material.xml b/InCallUI/res/anim/scale_in_material.xml
new file mode 100644
index 0000000..7f2cf2a
--- /dev/null
+++ b/InCallUI/res/anim/scale_in_material.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillEnabled="true"
+ android:fillAfter="true">
+ <scale
+
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:duration="325"
+ android:fromXScale="1.0"
+ android:toXScale="1.0"
+ android:fromYScale="1.0"
+ android:toYScale="0.0"
+ android:fillAfter="false"
+ />
+
+ <scale
+
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:startOffset="75"
+ android:duration="300"
+ android:fromXScale="1.0"
+ android:toXScale="0.0"
+ android:fromYScale="1.0"
+ android:toYScale="1.0"
+ android:fillAfter="false"
+ />
+
+</set>
diff --git a/InCallUI/res/anim/scale_out.xml b/InCallUI/res/anim/scale_out.xml
new file mode 100644
index 0000000..191f275
--- /dev/null
+++ b/InCallUI/res/anim/scale_out.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillEnabled="true"
+ android:fillAfter="true">
+ <scale
+
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromXScale="0.0"
+ android:toXScale="1.0"
+ android:fromYScale="0.0"
+ android:toYScale="1.0"
+ android:fillAfter="false"
+ />
+
+</set>
\ No newline at end of file
diff --git a/InCallUI/res/anim/scale_out_material.xml b/InCallUI/res/anim/scale_out_material.xml
new file mode 100644
index 0000000..ac8959c
--- /dev/null
+++ b/InCallUI/res/anim/scale_out_material.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillEnabled="true"
+ android:fillAfter="true">
+ <scale
+
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromXScale="0.0"
+ android:duration="275"
+ android:toXScale="1.0"
+ android:fromYScale="1.0"
+ android:toYScale="1.0"
+ android:fillAfter="false"
+ />
+
+ <scale
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:startOffset="25"
+ android:duration="350"
+ android:fromXScale="1.0"
+ android:toXScale="1.0"
+ android:fromYScale="0.0"
+ android:toYScale="1.0"
+ android:fillAfter="false"
+ />
+
+</set>
diff --git a/InCallUI/res/drawable-xxhdpi/enrich_details_bg.xml b/InCallUI/res/drawable-xxhdpi/enrich_details_bg.xml
new file mode 100644
index 0000000..38cdf18
--- /dev/null
+++ b/InCallUI/res/drawable-xxhdpi/enrich_details_bg.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:bottomLeftRadius="@dimen/card_corner_radius"
+ android:bottomRightRadius="@dimen/card_corner_radius"/>
+ <solid android:color="@color/enrichDetailsBackground"/>
+</shape>
\ No newline at end of file
diff --git a/InCallUI/res/drawable-xxhdpi/ic_rcs_collpase.png b/InCallUI/res/drawable-xxhdpi/ic_rcs_collpase.png
new file mode 100644
index 0000000..6d70ec1
--- /dev/null
+++ b/InCallUI/res/drawable-xxhdpi/ic_rcs_collpase.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_rcs_expand.png b/InCallUI/res/drawable-xxhdpi/ic_rcs_expand.png
new file mode 100644
index 0000000..1fac46d
--- /dev/null
+++ b/InCallUI/res/drawable-xxhdpi/ic_rcs_expand.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxxhdpi/ic_enrich_call_white_24dp.png b/InCallUI/res/drawable-xxxhdpi/ic_enrich_call_white_24dp.png
new file mode 100644
index 0000000..936dd6c
--- /dev/null
+++ b/InCallUI/res/drawable-xxxhdpi/ic_enrich_call_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable/normal_call_bg.xml b/InCallUI/res/drawable/normal_call_bg.xml
new file mode 100644
index 0000000..7c4bca9
--- /dev/null
+++ b/InCallUI/res/drawable/normal_call_bg.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:topRightRadius="@dimen/card_corner_radius"
+ android:topLeftRadius="@dimen/card_corner_radius" />
+ <solid android:color="@color/normalCallBackground"/>
+</shape>
diff --git a/InCallUI/res/drawable/ring.xml b/InCallUI/res/drawable/ring.xml
new file mode 100644
index 0000000..201b128
--- /dev/null
+++ b/InCallUI/res/drawable/ring.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <stroke
+ android:width="1dp"
+ android:color="@android:color/white"/>
+
+ <size
+ android:width="52dp"
+ android:height="52dp"/>
+</shape>
diff --git a/InCallUI/res/drawable/urgent_call_bg.xml b/InCallUI/res/drawable/urgent_call_bg.xml
new file mode 100644
index 0000000..69b10da
--- /dev/null
+++ b/InCallUI/res/drawable/urgent_call_bg.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:topRightRadius="@dimen/card_corner_radius"
+ android:topLeftRadius="@dimen/card_corner_radius" />
+ <solid android:color="@color/urgentCallBackground"/>
+</shape>
diff --git a/InCallUI/res/layout-h400dp/call_card_fragment.xml b/InCallUI/res/layout-h400dp/call_card_fragment.xml
index ccff385..d75075c 100644
--- a/InCallUI/res/layout-h400dp/call_card_fragment.xml
+++ b/InCallUI/res/layout-h400dp/call_card_fragment.xml
@@ -123,12 +123,19 @@
</FrameLayout>
+ <fragment android:name="com.android.incallui.RcsCallFragment"
+ android:id="@+id/enrichcall_container"
+ android:layout_gravity="top|center_horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/primary_call_info_container" />
+
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerHorizontal="true"
- android:layout_below="@id/primary_call_info_container">
+ android:layout_below="@id/enrichcall_container">
<include layout="@layout/manage_conference_call_button"
android:layout_width="match_parent"
diff --git a/InCallUI/res/layout-w500dp-land/call_card_fragment.xml b/InCallUI/res/layout-w500dp-land/call_card_fragment.xml
index 343c4da..4d59cb7 100644
--- a/InCallUI/res/layout-w500dp-land/call_card_fragment.xml
+++ b/InCallUI/res/layout-w500dp-land/call_card_fragment.xml
@@ -82,6 +82,12 @@
</FrameLayout>
+ <fragment android:name="com.android.incallui.RcsCallFragment"
+ android:id="@+id/enrichcall_container"
+ android:layout_gravity="top|center_horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<fragment android:name="com.android.incallui.VideoCallFragment"
android:layout_alignParentStart="true"
android:layout_gravity="start|center_vertical"
diff --git a/InCallUI/res/layout/call_card_fragment.xml b/InCallUI/res/layout/call_card_fragment.xml
index 2a4c3e2..3a1c61d 100644
--- a/InCallUI/res/layout/call_card_fragment.xml
+++ b/InCallUI/res/layout/call_card_fragment.xml
@@ -98,12 +98,19 @@
</FrameLayout>
+ <fragment android:name="com.android.incallui.RcsCallFragment"
+ android:id="@+id/enrichcall_container"
+ android:layout_gravity="top|center_horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/primary_call_info_container" />
+
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerHorizontal="true"
- android:layout_below="@id/primary_call_info_container">
+ android:layout_below="@id/enrichcall_container">
<include layout="@layout/manage_conference_call_button"
android:layout_width="match_parent"
diff --git a/InCallUI/res/layout/enrich_call_content.xml b/InCallUI/res/layout/enrich_call_content.xml
new file mode 100644
index 0000000..233e901
--- /dev/null
+++ b/InCallUI/res/layout/enrich_call_content.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ android:gravity="center">
+
+ <LinearLayout
+ android:id="@+id/enrich_main"
+ android:layout_width="@dimen/enrich_min_width"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="@dimen/main_layout_margin_top"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/call_type"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/call_type_text_height"
+ android:background="@drawable/normal_call_bg"
+ android:gravity="center_vertical"
+ android:paddingLeft="@dimen/call_type_padding_left"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/call_type_font_size" />
+
+
+ <LinearLayout
+ android:id="@+id/enrich_details"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/enrich_details_bg"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/notes"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notes_text_height"
+ android:ellipsize="end"
+ android:gravity="center_vertical"
+ android:maxLines="1"
+ android:paddingLeft="5dp"
+ android:textColor="@android:color/black"
+ android:textSize="@dimen/notes_font_size" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+id/picture"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/mini_view_picture_height"
+ android:layout_weight="1"
+ android:paddingLeft="@dimen/picture_layout_padding"
+ android:layout_marginTop="@dimen/picture_layout_padding"
+ android:layout_marginBottom="@dimen/picture_layout_padding"
+ android:scaleType="centerCrop"/>
+
+ <Space
+ android:layout_width="@dimen/picture_layout_padding"
+ android:layout_height="wrap_content" />
+
+ <ImageView
+ android:id="@+id/map"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/mini_view_picture_height"
+ android:layout_weight="1"
+ android:paddingRight="@dimen/picture_layout_padding"
+ android:layout_marginTop="@dimen/picture_layout_padding"
+ android:layout_marginBottom="@dimen/picture_layout_padding"
+ android:scaleType="centerCrop" />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="@dimen/button_layout_width"
+ android:layout_height="@dimen/button_layout_height"
+ android:layout_alignLeft="@+id/enrich_main"
+ android:layout_marginLeft="@dimen/button_margin_left">
+
+ <ImageView
+ android:id="@+id/ring_anim"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:src="@drawable/ring" />
+
+
+ <ImageView
+ android:id="@+id/expand_collapse"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:src="@drawable/ic_rcs_collpase" />
+ </RelativeLayout>
+</RelativeLayout>
+
diff --git a/InCallUI/res/values/colors.xml b/InCallUI/res/values/colors.xml
index 99cf7b0..3764014 100644
--- a/InCallUI/res/values/colors.xml
+++ b/InCallUI/res/values/colors.xml
@@ -122,4 +122,11 @@
<!-- White background for dialer -->
<color name="background_dialer_white">#ffffff</color>
+
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+ <color name="urgentCallBackground">#f40707</color>
+ <color name="normalCallBackground">#0ab5e0</color>
+ <color name="enrichDetailsBackground">#FFFFFF</color>
</resources>
diff --git a/InCallUI/res/values/dimens.xml b/InCallUI/res/values/dimens.xml
index 3a926b6..357c582 100644
--- a/InCallUI/res/values/dimens.xml
+++ b/InCallUI/res/values/dimens.xml
@@ -151,4 +151,28 @@
<dimen name="business_contact_context_detail_font_size">12sp</dimen>
<dimen name="contact_context_small_photo_size">40dp</dimen>
+
+ <!-- RCS dimensions. -->
+ <dimen name="enrich_miniview_layout_height">126.6dp</dimen>
+ <dimen name="enrich_miniview_layout_width">306.6dp</dimen>
+ <dimen name="enrich_detail_margin">0dp</dimen>
+ <dimen name="enrich_min_width">306.6dp</dimen>
+ <dimen name="call_type_font_size">18dp</dimen>
+ <dimen name="notes_font_size">15dp</dimen>
+ <dimen name="max_height_for_picture">60dp</dimen>
+ <dimen name="card_corner_radius">5dp</dimen>
+ <dimen name="call_type_text_height">33dp</dimen>
+ <dimen name="notes_text_height">40dp</dimen>
+ <dimen name="call_type_padding_left">60dp</dimen>
+ <dimen name="picture_layout_padding">3.3dp</dimen>
+ <dimen name="button_margin_top">13dp</dimen>
+ <dimen name="button_margin_left">-22dp</dimen>
+ <dimen name="enrich_details_margin_left">17dp</dimen>
+ <integer name="expand_duration">375</integer>
+ <integer name="collapse_duration">300</integer>
+ <dimen name="button_layout_width">64dp</dimen>
+ <dimen name="button_layout_height">64dp</dimen>
+ <dimen name="main_layout_margin_top">17dp</dimen>
+ <dimen name="mini_view_picture_height">50dp</dimen>
+ <dimen name="expanded_view_picture_height">143.3dp</dimen>
</resources>
diff --git a/InCallUI/res/values/qtistrings.xml b/InCallUI/res/values/qtistrings.xml
index 8f068a0..afa29ab 100644
--- a/InCallUI/res/values/qtistrings.xml
+++ b/InCallUI/res/values/qtistrings.xml
@@ -191,4 +191,27 @@
<string name="video_call_picture_mode_incoming_video">Incoming Video</string>
<string name="video_call_picture_mode_cancel_option">Cancel</string>
<string name="video_call_picture_mode_save_option">Save</string>
+
+
+ <!-- Enrich call strings -->
+ <string name="notification_incoming_enrich_call_high_priority">Incoming urgent call</string>
+ <string name="notification_incoming_enrich_call_normal_priority">Incoming rich call</string>
+ <string name="notification_on_hold_enrich_call_high_priority">Urgent call on hold</string>
+ <string name="notification_on_hold_enrich_call_normal_priority">Rich call on hold</string>
+ <string name="notification_dialing_enrich_call_high_priority">Dialing urgent call</string>
+ <string name="notification_dialing_enrich_call_normal_priority">Dialing rich call</string>
+ <string name="notification_ongoing_enrich_call_high_priority">Ongoing urgent call</string>
+ <string name="notification_ongoing_enrich_call_normal_priority">Ongoing rich call</string>
+ <string name="normal_enrich_call">RICH CALL</string>
+ <string name="urgent_enrich_call">URGENT CALL</string>
+ <string name="rich_call_failed">Rich call failed</string>
+ <string name="urgent_call_failed">Urgent call failed</string>
+ <string name="can_not_launch_image_application">Can not find any application to show shared image</string>
+ <string name="can_not_launch_maps_application">Can not find any application to show maps location</string>
+ <string name="enrich_call_permission">Access Enriched call</string>
+ <string name="access_permission_message">Phone App requires access to photos,
+ media & files on your device to enable Rich Calling. To continue, tap \'YES\'
+ below & \'ALLOW\' on the next screen.</string>
+ <string name="yes">Yes</string>
+ <string name="no">No</string>
</resources>
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index ff90272..c3c9a81 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -1165,6 +1165,11 @@
}
@Override
+ public void onAnswerViewGrab(boolean isGrabbed) {
+ // No-op - required for RcsCallPresenter.
+ }
+
+ @Override
public void onIncomingVideoAvailabilityChanged(boolean isAvailable) {
Log.d(this, "onIncomingVideoAvailabilityChanged: available = " + isAvailable);
if (mPrimary == null) {
diff --git a/InCallUI/src/com/android/incallui/GlowPadWrapper.java b/InCallUI/src/com/android/incallui/GlowPadWrapper.java
index c88a984..ab4660d 100644
--- a/InCallUI/src/com/android/incallui/GlowPadWrapper.java
+++ b/InCallUI/src/com/android/incallui/GlowPadWrapper.java
@@ -95,6 +95,7 @@
@Override
public void onGrabbed(View v, int handle) {
Log.d(this, "onGrabbed()");
+ InCallPresenter.getInstance().notifyAnswerViewGrabChanged(true);
stopPing();
}
@@ -106,6 +107,7 @@
} else {
startPing();
}
+ InCallPresenter.getInstance().notifyAnswerViewGrabChanged(false);
}
@Override
diff --git a/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java b/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java
index bdc46e1..c413b07 100644
--- a/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java
+++ b/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java
@@ -30,6 +30,7 @@
package com.android.incallui;
import android.app.AlertDialog;
+import android.graphics.Color;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
@@ -401,7 +402,7 @@
});
if (VideoUtils.isIncomingVideoCall(call)) {
- alertDialog.setNegativeButton(R.string.low_battery_convert, new OnClickListener() {
+ alertDialog.setPositiveButton(R.string.low_battery_convert, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d(this, "displayLowBatteryAlert answer as Voice Call");
@@ -411,7 +412,7 @@
});
alertDialog.setMessage(R.string.low_battery_msg);
- alertDialog.setPositiveButton(R.string.low_battery_yes, new OnClickListener() {
+ alertDialog.setNegativeButton(R.string.low_battery_yes, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d(this, "displayLowBatteryAlert answer as Video Call");
@@ -420,7 +421,7 @@
}
});
} else if (VideoUtils.isOutgoingVideoCall(call)) {
- alertDialog.setNegativeButton(R.string.low_battery_convert, new OnClickListener() {
+ alertDialog.setPositiveButton(R.string.low_battery_convert, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d(this, "displayLowBatteryAlert place Voice Call");
@@ -437,7 +438,7 @@
});
alertDialog.setMessage(R.string.low_battery_msg);
- alertDialog.setPositiveButton(R.string.low_battery_yes, new OnClickListener() {
+ alertDialog.setNegativeButton(R.string.low_battery_yes, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d(this, "displayLowBatteryAlert place Video Call");
@@ -453,8 +454,8 @@
if (QtiCallUtils.hasVoiceCapabilities(call)) {
//active video call can be downgraded to voice
alertDialog.setMessage(R.string.low_battery_msg);
- alertDialog.setPositiveButton(R.string.low_battery_yes, null);
- alertDialog.setNegativeButton(R.string.low_battery_convert, new OnClickListener() {
+ alertDialog.setNegativeButton(R.string.low_battery_yes, null);
+ alertDialog.setPositiveButton(R.string.low_battery_convert, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d(this, "displayLowBatteryAlert downgrading to voice call");
@@ -465,8 +466,8 @@
/* video call doesn't have downgrade capabilities, so alert the user
with a hangup dialog*/
alertDialog.setMessage(R.string.low_battery_hangup_msg);
- alertDialog.setNegativeButton(R.string.low_battery_no, null);
- alertDialog.setPositiveButton(R.string.low_battery_yes, new OnClickListener() {
+ alertDialog.setPositiveButton(R.string.low_battery_no, null);
+ alertDialog.setNegativeButton(R.string.low_battery_yes, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d(this, "displayLowBatteryAlert hanging up the call: " + call);
@@ -495,6 +496,11 @@
mAlert.setCanceledOnTouchOutside(false);
mAlert.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
mAlert.show();
+ /*
+ * By default both the buttons will have same color. In case we want to have different color
+ * we need to set specifically.
+ */
+ mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(Color.BLACK);
}
/*
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 1bc3b4f..555f92c 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -732,7 +732,9 @@
if (CallList.getInstance().isDsdaEnabled() && (mInCallActivity != null)) {
mInCallActivity.updateDsdaTab();
}
- wakeUpScreen();
+ if (isActivityStarted()) {
+ wakeUpScreen();
+ }
}
@Override
@@ -2019,6 +2021,16 @@
return mThemeColors;
}
+ /**
+ * this function will be called from glowpadwrapper to notify
+ * incallpresenter when user touch or release the answer view.
+ */
+ public void notifyAnswerViewGrabChanged(boolean isGrabbed) {
+ for (InCallEventListener listener : mInCallEventListeners) {
+ listener.onAnswerViewGrab(isGrabbed);
+ }
+ }
+
private MaterialPalette getColorsFromCall(Call call) {
if (call == null) {
return getColorsFromPhoneAccountHandle(mPendingPhoneAccountHandle);
@@ -2150,6 +2162,7 @@
public void updatePrimaryCallState();
public void onIncomingVideoAvailabilityChanged(boolean isAvailable);
public void onSendStaticImageStateChanged(boolean isEnabled);
+ public void onAnswerViewGrab(boolean isGrabbed);
}
public interface InCallUiListener {
diff --git a/InCallUI/src/com/android/incallui/InCallRcsImageDownloader.java b/InCallUI/src/com/android/incallui/InCallRcsImageDownloader.java
new file mode 100644
index 0000000..e119c17
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallRcsImageDownloader.java
@@ -0,0 +1,125 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.incallui;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+
+import org.codeaurora.rcscommon.FetchImageCallBack;
+import org.codeaurora.rcscommon.RcsManager;
+
+/**
+ * This class InCallRcsImageDownloader is used to get the RCS shared image and
+ * RCS shared location image from the RCSService. This class will call the RCSManager
+ * to fetch Shared Image and Location image.
+ */
+public class InCallRcsImageDownloader {
+
+ private static final int FETCH_LOCATION_IMAGE = 1;
+ private static final int UPDATE_IMAGE_DETAILS = 2;
+
+ public static interface FetchImageListener {
+ public void onFetchedImage(byte[] arr);
+ }
+
+ private class ImageDetails {
+ FetchImageListener mListener;
+ byte[] result;
+ int width, height;
+ double lat, lon;
+
+ ImageDetails(int width, int height, double lat,
+ double lon, FetchImageListener listener) {
+ mListener = listener;
+ this.width = width;
+ this.height = height;
+ this.lat = lat;
+ this.lon = lon;
+ }
+ }
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case FETCH_LOCATION_IMAGE:
+ fetchLocationImage((ImageDetails) msg.obj);
+ break;
+ case UPDATE_IMAGE_DETAILS:
+ ImageDetails details = (ImageDetails) msg.obj;
+ if (details.mListener != null) {
+ details.mListener.onFetchedImage(details.result);
+ }
+ break;
+ }
+ }
+ };
+
+ private Context mContext;
+ private RcsManager mRcsManager;
+
+ InCallRcsImageDownloader(Context context) {
+ mContext = context;
+ mRcsManager = RcsManager.getInstance(context);
+ }
+
+ /**
+ * request to get the location image.
+ * the image result will be returned asynchronously.
+ * @param int width, int height, double lat,
+ * double lon, FetchImageListener listener.
+ */
+ public void requestFetchLocationImage(int width, int height, double lat,
+ double lon, FetchImageListener listener) {
+ ImageDetails locImgDetails = new ImageDetails(width,
+ height, lat, lon, listener);
+
+ Message msg = new Message();
+ msg.what = FETCH_LOCATION_IMAGE;
+ msg.obj = locImgDetails;
+ mHandler.sendMessage(msg);
+ }
+
+ private void fetchLocationImage(final ImageDetails details) {
+ FetchImageCallBack rcsMapCallBack = new FetchImageCallBack.Stub() {
+ public void onImageFetched( byte[] image) {
+ Log.d(this,"onStaticMapFeatch " + image);
+ details.result = image;
+ Message msg = new Message();
+ msg.what = UPDATE_IMAGE_DETAILS;
+ msg.obj = details;
+ mHandler.sendMessage(msg);
+ }
+ };
+
+ mRcsManager.fetchStaticMap(details.lat, details.lon,
+ details.width, details.height, rcsMapCallBack);
+ }
+
+}
diff --git a/InCallUI/src/com/android/incallui/InCallRcsUtils.java b/InCallUI/src/com/android/incallui/InCallRcsUtils.java
new file mode 100644
index 0000000..546aae1
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallRcsUtils.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.incallui;
+
+import android.content.Context;
+import android.telecom.Call.Details;
+import android.text.style.ForegroundColorSpan;
+import android.text.SpannableStringBuilder;
+
+import org.codeaurora.rcscommon.CallComposerData;
+
+/**
+ * This class contains Enrich call specific utiltity functions.
+ */
+public class InCallRcsUtils {
+
+ /**
+ * Verify if current call is of enrich call type.
+ * Excludes all other call types [video call, video call upgrade request, conference call
+ * wifi call]
+ *
+ * @param call
+ * @param context
+ * @return
+ */
+ public static boolean isEnrichCall(Call call, Context context) {
+
+ final boolean isVideoUpgradeRequest =
+ call.getSessionModificationState()
+ == Call.SessionModificationState.
+ RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
+ RcsCallInfo callInfo = RcsCallHandler.getInstance().getRcsCallInfo(call);
+ return callInfo != null && callInfo.isEnrichedCall()
+ && !call.hasProperty(Details.PROPERTY_WIFI)
+ && !call.isVideoCall(context)
+ && !call.isConferenceCall()
+ && !isVideoUpgradeRequest;
+ }
+
+ /**
+ * Get enrich call notification string for HIGH and NORMAL
+ * priority enrich call based on different call states
+ *
+ * @param call
+ * @param isIncomingOrWaiting
+ * @return
+ */
+ public static int getEnrichContentString(Call call, boolean isIncomingOrWaiting) {
+ int resId = R.string.notification_ongoing_enrich_call_normal_priority;
+
+ RcsCallInfo callInfo = RcsCallHandler.getInstance().getRcsCallInfo(call);
+ CallComposerData data = callInfo != null ? callInfo.getCallComposerData() : null;
+ if (isIncomingOrWaiting) {
+ if (data != null && data.getPriority() == CallComposerData.PRIORITY.HIGH) {
+ resId = R.string.notification_incoming_enrich_call_high_priority;
+ } else {
+ resId = R.string.notification_incoming_enrich_call_normal_priority;
+ }
+ } else if (call.getState() == Call.State.ONHOLD) {
+ if (data != null && data.getPriority() == CallComposerData.PRIORITY.HIGH) {
+ resId = R.string.notification_on_hold_enrich_call_high_priority;
+ } else {
+ resId = R.string.notification_on_hold_enrich_call_normal_priority;
+ }
+
+ } else if (Call.State.isDialing(call.getState())) {
+ if (data != null && data.getPriority() == CallComposerData.PRIORITY.HIGH) {
+ resId = R.string.notification_dialing_enrich_call_high_priority;
+ } else {
+ resId = R.string.notification_dialing_enrich_call_normal_priority;
+ }
+
+ } else {
+ if (data != null && data.getPriority() == CallComposerData.PRIORITY.HIGH) {
+ resId = R.string.notification_ongoing_enrich_call_high_priority;
+ } else {
+ resId = R.string.notification_ongoing_enrich_call_normal_priority;
+ }
+ }
+
+ return resId;
+ }
+
+ /**
+ * Get coloured notification content string for enrich call based
+ * on enrich call priority
+ *
+ * @param context
+ * @param call
+ * @param content
+ * @return
+ */
+ public static SpannableStringBuilder getEnrichContentText(Context context, Call call,
+ String content) {
+ RcsCallInfo callInfo = RcsCallHandler.getInstance().getRcsCallInfo(call);
+ CallComposerData data = callInfo != null ? callInfo.getCallComposerData() : null;
+ SpannableStringBuilder styleContent =
+ new SpannableStringBuilder();
+ if (data != null && data.getPriority() == CallComposerData.PRIORITY.HIGH) {
+ appendStyled(styleContent, content,
+ new ForegroundColorSpan(context.getResources().
+ getColor(R.color.urgentCallBackground)));
+ } else {
+ appendStyled(styleContent, content,
+ new ForegroundColorSpan(context.getResources().
+ getColor(R.color.normalCallBackground)));
+ }
+
+ return styleContent;
+ }
+
+ /**
+ * Handle color spans for string
+ *
+ * Set styles for subparts in a string. Subparts in a string can be of different styles
+ * like color, bold, italic etc..
+ *
+ * @param builder
+ * @param str
+ * @param spans
+ */
+ private static void appendStyled(SpannableStringBuilder builder,
+ String str, Object... spans) {
+ builder.append(str);
+ for (Object span : spans) {
+ builder.setSpan(span, builder.length() - str.length(), builder.length(), 0);
+ }
+ }
+}
diff --git a/InCallUI/src/com/android/incallui/RcsCallFragment.java b/InCallUI/src/com/android/incallui/RcsCallFragment.java
new file mode 100644
index 0000000..d9d37b3
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/RcsCallFragment.java
@@ -0,0 +1,619 @@
+/**
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.incallui;
+
+import android.app.AlertDialog;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.Manifest;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.TypedValue ;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.codeaurora.rcscommon.CallComposerData;
+
+/**
+ * RcsCallFragment class is used to display the RCS call content on the incall screen.
+ */
+public class RcsCallFragment extends
+ BaseFragment<RcsCallPresenter, RcsCallPresenter.RcsCallUi> implements
+ RcsCallPresenter.RcsCallUi, View.OnClickListener {
+
+ private static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 0;
+ private static final String PREFERENCE_SHOWN_INFO_DLG_KEY = "RCS_SHOWN_INFO_DLG";
+
+ private TextView mPriorityView, mSubject;
+ private ImageView mMapView, mImageView;
+ private View mRootView = null, mEnrichDetailLayout;
+ private ImageView mExpandCollapseBtn, mRingAnimView;
+ private boolean mIsDataAvailable;
+ private boolean mIsPresmissionReqPending;
+ private static RcsCallPresenter sRcsCallPresenter;
+
+ private RcsCallPresenter.ImageBitmapListener mImageViewListener
+ = new RcsCallPresenter.ImageBitmapListener() {
+ public void onFetchBitmap(Bitmap bitmap) {
+ mImageView.setImageBitmap(bitmap);
+ }
+ };
+
+ private RcsCallPresenter.ImageBitmapListener mMapViewListener
+ = new RcsCallPresenter.ImageBitmapListener() {
+ public void onFetchBitmap(Bitmap bitmap) {
+ mMapView.setImageBitmap(bitmap);
+ }
+ };
+
+ @Override
+ public RcsCallPresenter createPresenter() {
+ sRcsCallPresenter = new RcsCallPresenter();
+ return sRcsCallPresenter;
+ }
+
+ /**
+ * getPresenterInstance will return a singleton instance of RcsCallPresenter
+ *
+ * @return RcsCallPresenter
+ */
+ public static RcsCallPresenter getPresenterInstance() {
+ return sRcsCallPresenter;
+ }
+
+ @Override
+ public RcsCallPresenter.RcsCallUi getUi() {
+ return this;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.d(this, "onCreate : ");
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ Log.d(this, "onCreateView : ");
+ mRootView = inflater.inflate(R.layout.enrich_call_content,
+ container, false);
+
+ mPriorityView = (TextView) mRootView.findViewById(R.id.call_type);
+
+ mMapView = (ImageView) mRootView.findViewById(R.id.map);
+ mMapView.setOnClickListener(this);
+
+ mImageView = (ImageView) mRootView.findViewById(R.id.picture);
+ mImageView.setOnClickListener(this);
+
+ mSubject = (TextView) mRootView.findViewById(R.id.notes);
+ mSubject.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ showDetailedNotes(mSubject.getText().toString());
+ }
+ });
+
+ mExpandCollapseBtn= (ImageView) mRootView.findViewById(R.id.expand_collapse);
+
+ mRingAnimView = (ImageView) mRootView.findViewById(R.id.ring_anim);
+ mRingAnimView.setVisibility(View.INVISIBLE);
+
+ mEnrichDetailLayout = (LinearLayout) mRootView
+ .findViewById(R.id.enrich_details);
+ mExpandCollapseBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ animate(mEnrichDetailLayout, (ImageView) view, mRingAnimView,
+ !(mEnrichDetailLayout.getVisibility() == View.VISIBLE));
+ }
+ });
+
+ return mRootView;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ Log.d(this, "onActivityCreated");
+ super.onActivityCreated(savedInstanceState);
+ }
+
+ @Override
+ public void onResume() {
+ Log.d(this, "onResume");
+ super.onResume();
+ }
+
+ @Override
+ public void onClick(View view) {
+ Log.d(this,"onClick : " + view);
+ switch(view.getId()) {
+ case R.id.picture:
+ Uri uri = (Uri) mImageView.getTag();
+ if(uri != null){
+ getPresenter().onSharedImageClicked(uri);
+ }
+ break;
+ case R.id.map:
+ double[] arr = (double[]) mMapView.getTag();
+ if(arr != null && arr.length == 2) {
+ getPresenter().onMapImageClicked(arr[0], arr[1]);
+ }
+ break;
+ }
+ }
+
+ @Override
+ public Context getContext() {
+ return getActivity();
+ }
+
+ @Override
+ public void updateCallComposerData(RcsCallInfo info) {
+ CallComposerData data = info != null ? info.getCallComposerData() : null;
+ Log.d(this, "updateCallComposerData : " + data);
+ if (data == null) {
+ return;
+ }
+ mIsDataAvailable = false;
+
+ //EnrichCall Subject View update
+ if (TextUtils.isEmpty(data.getSubject())) {
+ mSubject.setVisibility(View.GONE);
+ } else {
+ mIsDataAvailable = true;
+ mSubject.setVisibility(View.VISIBLE);
+ }
+ mSubject.setText(data.getSubject());
+
+ //EnrichCall priority view update
+ if (data.getPriority() == CallComposerData.PRIORITY.NORMAL) {
+ mPriorityView.setBackgroundResource(R.drawable.normal_call_bg);
+ mPriorityView.setText(getString(R.string.normal_enrich_call));
+ } else {
+ mPriorityView.setBackgroundResource(R.drawable.urgent_call_bg);
+ mPriorityView.setText(getString(R.string.urgent_enrich_call));
+ }
+
+ //Location View update
+ if (data.isValidLocation()) {
+ mIsDataAvailable = true;
+ mMapView.setVisibility(View.VISIBLE);
+ } else {
+ mMapView.setVisibility(View.GONE);
+ }
+
+ if (info.getLocationImageArray() != null
+ && info.getLocationImageArray().length > 0) {
+ updateLocationImage(info.getLocationImageArray(), data.getLatitude(),
+ data.getLongitude());
+ }
+
+ boolean canReadExternalStorage = hasReadStoragePermission();
+ Log.d(this, "canReadExternalStorage : " + canReadExternalStorage);
+
+ //SharedImage view update
+ if (data.isValidSharedImageUri()) {
+ mIsDataAvailable = true;
+ mImageView.setVisibility(View.VISIBLE);
+ } else {
+ mImageView.setVisibility(View.GONE);
+ }
+ if (canReadExternalStorage && data.isValidSharedImageUri()) {
+ getPresenter().updateImageFromUri(data.getImageUri(), mImageViewListener);
+ mImageView.setTag(data.getImageUri());
+ }
+
+ if (!mIsDataAvailable) {
+ mExpandCollapseBtn.setVisibility(View.GONE);
+ } else {
+ mExpandCollapseBtn.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public int getMapImageViewWidth() {
+ return mMapView != null ? mMapView.getWidth() : 0;
+ }
+
+ @Override
+ public int getMapImageViewHeight() {
+ //consider showing map for max height as when call is answered we need to show
+ //full map image.
+ return (int) getResources().
+ getDimension(R.dimen.expanded_view_picture_height);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ Log.d(this, "setEnabled : " + enabled);
+ if (mRootView == null) {
+ return;
+ }
+ mRootView.setEnabled(enabled);
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ Log.d(this, "setVisible : " + visible);
+ if (mRootView == null) {
+ return;
+ }
+ mRootView.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void showSmallView(boolean showSmall) {
+ Log.d(this, "showSmallView : " + showSmall);
+ if (mRootView == null) {
+ return;
+ }
+ setMiniUi(mEnrichDetailLayout, showSmall);
+ }
+
+ @Override
+ public void showEnrichCallFailed(CallComposerData.PRIORITY priority) {
+ if (priority == CallComposerData.PRIORITY.HIGH) {
+ Toast.makeText(getActivity(), R.string.urgent_call_failed,
+ Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(getActivity(), R.string.rich_call_failed,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ public void showEnrichCallDetail() {
+ if (!(mEnrichDetailLayout.getVisibility() == View.VISIBLE)) {
+ expandCollapse(mEnrichDetailLayout, mExpandCollapseBtn, mRingAnimView, true);
+ }
+ }
+
+ @Override
+ public void hideEnrichCallDetail() {
+ if (mEnrichDetailLayout.getVisibility() == View.VISIBLE) {
+ expandCollapse(mEnrichDetailLayout, mExpandCollapseBtn, mRingAnimView, false);
+ }
+ }
+
+ private void updateLocationImage(final byte[] arr, double lat, double lon) {
+ Log.d(this, "updateLocationImage ");
+ mMapView.setTag(new double[]{lat, lon});
+ getPresenter().updateImageFromBytes(arr, mMapViewListener);
+ }
+
+ /**
+ * Hide and show RCS content detailed view
+ *
+ * Additionally if detailed view is hidden ring animation will be shown.
+ *
+ * @param view parent view
+ * @param buttonView expand/collapse button view
+ * @param ringView view to show ring animation on expand/collapse button
+ * @param show hide/show detailed view with animation
+ */
+ private void expandCollapse(final View view, final ImageView buttonView,
+ final ImageView ringView, boolean show) {
+ /* Show a hide animation if view is visible or when the show variable is false */
+ if (view.getVisibility() == View.VISIBLE || !show) {
+ view.setVisibility(View.GONE);
+ buttonView.setImageResource(R.drawable.ic_rcs_expand);
+ if ((ringView != null) && mIsDataAvailable) {
+ Animation pulse = AnimationUtils.loadAnimation(getActivity(), R.anim.pulse);
+ ringView.startAnimation(pulse);
+ }
+ } else {
+ view.setVisibility(View.VISIBLE);
+ if (ringView != null) {
+ ringView.clearAnimation();
+ ringView.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ /**
+ * Animation function to hide show detailed view.
+ *
+ * When detailed view is shown Collapse button is shown
+ * when detaild view is hidden Expand button is shown.
+ *
+ * Additionally if detailed view is hidden ring animation will be shown.
+ *
+ * @param view parent view
+ * @param buttonView expand/collapse button view
+ * @param ringView view to show ring animation on expand/collapse button
+ * @param show hide/show detailed view with animation
+ */
+ private void animate(final View view, final ImageView buttonView,
+ final ImageView ringView, boolean show) {
+ /* Show a hide animation if view is visible or when the show variable is false */
+ if (view.getVisibility() == View.VISIBLE || !show) {
+
+ Animation animation = AnimationUtils.loadAnimation(getActivity(),
+ R.anim.scale_in);
+ //Setting animation duration for collapsing detailed view.
+ animation.setDuration(getResources()
+ .getInteger(R.integer.collapse_duration));
+ animation.setAnimationListener(new Animation.AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ view.setVisibility(View.GONE);
+ buttonView.setImageResource(R.drawable.ic_rcs_expand);
+ if ((ringView != null)
+ && mIsDataAvailable) {
+ Animation pulse = AnimationUtils.loadAnimation(getActivity(),
+ R.anim.pulse);
+ ringView.startAnimation(pulse);
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ view.startAnimation(animation);
+
+ } else {
+
+ view.setVisibility(View.VISIBLE);
+ Animation animation = AnimationUtils.loadAnimation(getActivity(),
+ R.anim.scale_out);
+ //Setting animation duration for expand detailed view.
+ animation.setDuration(getResources().getInteger(R.integer.
+ expand_duration));
+ animation.setAnimationListener(new Animation.AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ buttonView.setImageResource(R.drawable.ic_rcs_collpase);
+ if (ringView != null) {
+ ringView.clearAnimation();
+ ringView.setVisibility(View.GONE);
+ }
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ view.startAnimation(animation);
+ }
+
+
+ }
+
+ private void setMiniUi(View parent, boolean isMinimized) {
+
+ int pxMiniViewHeight = (int) getResources().
+ getDimension(R.dimen.mini_view_picture_height);
+ int pxExpandedViewHeight = (int) getResources().
+ getDimension(R.dimen.expanded_view_picture_height);
+
+
+ ImageView picture = (ImageView) parent.findViewById(R.id.picture);
+ ImageView map = (ImageView) parent.findViewById(R.id.map);
+
+ int dimensionInPx = pxExpandedViewHeight;
+
+ if (isMinimized) {
+ dimensionInPx = pxMiniViewHeight;
+ }
+
+ picture.getLayoutParams().height = dimensionInPx;
+ map.getLayoutParams().height = dimensionInPx;
+
+ map.requestLayout();
+ picture.requestLayout();
+
+ parent.requestLayout();
+
+ }
+
+ /**
+ * showDetailedNotes will be called to display the note message in a alert dialog so that
+ * full text can be shown in an dialog message.
+ *
+ * @param String message to display
+ */
+ private void showDetailedNotes(String message) {
+ AlertDialog alertDialog = null;
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(message);
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface
+ .OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ dialogInterface.dismiss();
+ }
+ });
+
+ alertDialog = builder.create();
+ alertDialog.show();
+ }
+
+ /**
+ * isInForground function can be used to know if the incall activity is showing or not.
+ *
+ * @return boolean, true if incall activity is showing else false.
+ */
+ public boolean isInForground() {
+ return InCallPresenter.getInstance().isShowingInCallUi();
+ }
+
+ /**
+ * hasReadStoragePermission function can be used to know if the access to read storage
+ * permission is granted or not.
+ *
+ * @return boolean, true if permission is granted else false.
+ */
+ public boolean hasReadStoragePermission() {
+ return getContext().checkPermission(
+ Manifest.permission.READ_EXTERNAL_STORAGE, Process.myPid(),
+ Process.myUid()) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * isPermissionPending function is used to know if request permission is under process of not.
+ *
+ * @return boolean, true if request permission is pending else false.
+ */
+ public boolean isPermissionPending() {
+ return mIsPresmissionReqPending;
+ }
+
+ /**
+ * requestReadStoragePermission function is used to initiate the process to show a dialog to
+ * user to grant the read strorage permission.
+ */
+ public synchronized void requestReadStoragePermission() {
+ Log.d(this, "requestReadStoragePermission");
+ mIsPresmissionReqPending = true;
+ boolean canReadExternalStorage = hasReadStoragePermission();
+ if (!canReadExternalStorage) {
+ if (canShowPermissionInfoDlg()) {
+ showAccessPermissionDialog();
+ } else {
+ requestAskPermission();
+ }
+ } else {
+ Log.d(this, "already permission granted");
+ getPresenter().onReadStoragePermission(true);
+ mIsPresmissionReqPending = false;
+ }
+ }
+
+ /**
+ * This function will show a pre request permission dialog from Incall application,
+ * and this dialog explains hte user that why we need to read storage permission.
+ * Once user presses yes in this dialog then it will ask the actual AOSP permission dialog.
+ * If user presses No then it will simply ignore.
+ *
+ * Note: this explanation dialog will be show to the user only 1 time to avoid showing it
+ * multiple times.
+ */
+ private void showAccessPermissionDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext(),
+ AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
+ builder.setMessage(R.string.access_permission_message)
+ .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ if (InCallPresenter.getInstance().isShowingInCallUi()) {
+ requestAskPermission();
+ }
+ }
+ }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ mIsPresmissionReqPending = false;
+ getPresenter().onReadStoragePermission(false);
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ shownPremissionInfoDlg();
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[],
+ int[] grantResults) {
+ Log.d(this, "onRequestPermissionsResult");
+ switch (requestCode) {
+ case MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE: {
+ // If request is cancelled, the result arrays are empty.
+ mIsPresmissionReqPending = false;
+ getPresenter().onReadStoragePermission(grantResults != null
+ && grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED);
+ }
+ }
+ }
+
+ /**
+ * Process AOSP request permission for read external storage permission.
+ */
+ private void requestAskPermission() {
+ requestPermissions(new String[] { Manifest.permission.READ_EXTERNAL_STORAGE},
+ MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
+ }
+
+ /**
+ * canShowPermissionInfoDlg function can be use to know if Incall application can show
+ * Permission info dialog.
+ *
+ * @return true if need to display, false if already display before.
+ */
+ private boolean canShowPermissionInfoDlg() {
+ SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
+ return !sharedPref.getBoolean(PREFERENCE_SHOWN_INFO_DLG_KEY, false);
+ }
+
+ /**
+ * shownPremissionInfoDlg function is to write the sharedpreference that request permission
+ * info dialog is already displayed.
+ */
+ private void shownPremissionInfoDlg() {
+ SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedPref.edit();
+ editor.putBoolean(PREFERENCE_SHOWN_INFO_DLG_KEY, true);
+ editor.commit();
+ }
+
+}
diff --git a/InCallUI/src/com/android/incallui/RcsCallHandler.java b/InCallUI/src/com/android/incallui/RcsCallHandler.java
new file mode 100644
index 0000000..5ee875b
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/RcsCallHandler.java
@@ -0,0 +1,176 @@
+/**
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.incallui;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * RcsCallHandler class is a singleton class
+ * and this class will handle the RcsCallInfo object of each call.
+ * One can get the RcsCallInfo by calling function getRcsCallInfo.
+ */
+public class RcsCallHandler implements CallList.Listener {
+
+ private static RcsCallHandler sRcsCallHandler;
+
+ /* This mRcsCallInfoContainer is used to save the call vs RcsCallInfo in a hash map
+ * the hasmap will be updated accordingly when a call is initiated and when a call is
+ * disconnected
+ */
+ private ConcurrentHashMap<Call, RcsCallInfo> mRcsCallInfoContainer
+ = new ConcurrentHashMap<Call, RcsCallInfo>();
+
+ /**
+ * makeing it as a singleton class.
+ */
+ private RcsCallHandler() {
+ CallList.getInstance().addListener(this);
+ }
+
+ /**
+ * getInstance will return a singleton instance of RcsCallHandler
+ *
+ * @return RcsCallHandler
+ */
+ public static RcsCallHandler getInstance() {
+ if (sRcsCallHandler == null) {
+ sRcsCallHandler = new RcsCallHandler();
+ }
+ return sRcsCallHandler;
+ }
+
+ /**
+ * getRcsCallInfo function will return the RcsCallInfo from mRcsCallInfoContainer
+ * if available.
+ *
+ * @param Call
+ * @return RcsCallInfo, null if did not find RcsCallInfo.
+ */
+ public RcsCallInfo getRcsCallInfo(Call call) {
+ if (call == null || !mRcsCallInfoContainer.containsKey(call)) {
+ log("getRcsCallInfo : returning null");
+ return null;
+ }
+ return mRcsCallInfoContainer.get(call);
+ }
+
+ /**
+ * addRcsCallInfo function will add a hashmap entry of Call and RcsCallInfo.
+ *
+ * @param Call
+ * @param RcsCallInfo
+ */
+ private void addRcsCallInfo(Call call, RcsCallInfo callInfo) {
+ log("addRcsCallInfo : " + call + " RcsCallInfo : " + callInfo);
+ if(call != null && callInfo != null) {
+ mRcsCallInfoContainer.put(call, callInfo);
+ }
+ log("mRcsCallInfoContainer content : " + mRcsCallInfoContainer);
+ }
+
+ /**
+ * removeRcsCallInfo function will remove the hashmap entry of Call.
+ *
+ * @param Call
+ */
+ private void removeRcsCallInfo(Call call) {
+ log("removeRcsCallInfo: " + call);
+ mRcsCallInfoContainer.remove(call);
+ log("mRcsCallInfoContainer content : " + mRcsCallInfoContainer);
+ }
+
+ private boolean requestAddRcsCallInfo(Call call) {
+ if (call != null && getRcsCallInfo(call) == null) {
+ addRcsCallInfo(call, new RcsCallInfo(call));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onIncomingCall(Call call) {
+ log("onIncomingCall: " + call);
+ if (requestAddRcsCallInfo(call)) {
+ log("Added IncomingCall");
+ return;
+ }
+ }
+
+ @Override
+ public void onUpgradeToVideo(Call call) {
+ }
+
+ @Override
+ public void onCallListChange(CallList callList) {
+ log("onCallListChange: " + callList);
+ /*
+ * get the call for each state and check if the call can be added to RCS call list.
+ * If it is successful then just return from the function.
+ */
+ Call call = callList.getIncomingCall();
+ if (requestAddRcsCallInfo(call)) {
+ log("Added IncomingCall");
+ return;
+ }
+
+ call = callList.getPendingOutgoingCall();
+ if (requestAddRcsCallInfo(call)) {
+ log("Added PendingOutgoing call");
+ return;
+ }
+
+ call = callList.getOutgoingCall();
+ if (requestAddRcsCallInfo(call)) {
+ log("Added outgoing call");
+ return;
+ }
+
+ call = callList.getActiveCall();
+ if (requestAddRcsCallInfo(call)) {
+ log("Added active call");
+ return;
+ }
+
+ call = callList.getBackgroundCall();
+ if (requestAddRcsCallInfo(call)) {
+ log("Added Background call");
+ return;
+ }
+ }
+
+ @Override
+ public void onDisconnect(Call call) {
+ log("onDisconnect: " + call);
+ removeRcsCallInfo(call);
+ }
+
+ private void log(String msg) {
+ Log.d(this,msg);
+ }
+}
diff --git a/InCallUI/src/com/android/incallui/RcsCallInfo.java b/InCallUI/src/com/android/incallui/RcsCallInfo.java
new file mode 100644
index 0000000..4e9af0d
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/RcsCallInfo.java
@@ -0,0 +1,168 @@
+/**
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.incallui;
+
+import android.content.Context;
+import org.codeaurora.rcscommon.CallComposerData;
+import org.codeaurora.rcscommon.RcsManager;
+import android.os.Bundle;
+/**
+ * RcsCallInfo will contain the RCS call data with location and shared image data.
+ * Each object of this class will be associated with the a perticular call
+ * which is sent in constructor.
+ */
+public class RcsCallInfo {
+
+ public enum AccessPermissionStatus {
+ UNKNOWN, GRANTED, NOT_GRANTED
+ }
+
+ private byte[] mLocationImageArray = null;
+
+ private boolean isLocationImageReqested = false;
+
+ private Call mCall;
+
+ private AccessPermissionStatus mPermissionStatus = AccessPermissionStatus.UNKNOWN;
+
+ /**
+ * Default constructor can not be called as Call object is mandatory to initialize or create
+ * RcsCallInfo object.
+ */
+ private RcsCallInfo() {
+ }
+
+ /**
+ * Constructor of RcsCallInfo
+ * @param : Call object to handle Enrich call data operations.
+ */
+ public RcsCallInfo(Call call) {
+ mCall = call;
+ }
+
+ /**
+ * Request to get the location image
+ *
+ * @param : int width of the image which need to be fetched
+ * @param : int height of the image which need to be fetched
+ * @param : Context
+ */
+ public void requestRcsLocationImage(int width, int height, Context context) {
+ Log.d(this,"requestRcsLocationImage");
+ if (!isLocationImageReqested) {
+ isLocationImageReqested = true;
+ CallComposerData data = getCallComposerData();
+ new InCallRcsImageDownloader(context)
+ .requestFetchLocationImage(width, height, data.getLatitude(),
+ data.getLongitude(), new InCallRcsImageDownloader.
+ FetchImageListener() {
+
+ public void onFetchedImage(byte[] arr) {
+ mLocationImageArray = arr;
+ if (RcsCallFragment.getPresenterInstance() != null) {
+ RcsCallFragment.getPresenterInstance().forceUpdateUi();
+ }
+ }
+ });
+ } else {
+ Log.d(this, "isLocationImageReqested : " + isLocationImageReqested);
+ }
+ }
+
+ /**
+ * check if any operations are pending to fetch location image.
+ * @return boolean true if pending else false.
+ */
+ public boolean isLocImageReqested() {
+ return isLocationImageReqested;
+ }
+
+ /**
+ * check if the call is enrich call or not
+ * @return boolean true if Enrich call else false
+ */
+ public boolean isEnrichedCall() {
+ return getCallComposerData() != null ? getCallComposerData().isValid() : false;
+ }
+
+ /**
+ * get the callcomposerdata.
+ * @return CallComposerData, null if the call is not a RCS call. else the actual
+ * RCS call content(CallComposerData)
+ */
+ public CallComposerData getCallComposerData() {
+ if (mCall == null || mCall.getTelecomCall() == null) {
+ return null;
+ }
+ Bundle extras = mCall.getTelecomCall().getDetails().getExtras();
+ CallComposerData data = null;
+ if (extras == null ||
+ extras.getBundle(RcsManager.ENRICH_CALL_INTENT_EXTRA) == null) {
+ extras = mCall.getTelecomCall().getDetails().getIntentExtras();
+ if (extras != null) {
+ data = new CallComposerData(extras.getBundle(
+ RcsManager.ENRICH_CALL_INTENT_EXTRA));
+ Log.d(this, "getCallComposerData() from Intent Extras: " + data);
+ }
+ } else {
+ data = new CallComposerData(
+ extras.getBundle(RcsManager.ENRICH_CALL_INTENT_EXTRA));
+ Log.d(this, "getCallComposerData() from Extras: " + data);
+ }
+ return data;
+ }
+
+ /**
+ * get the permission status to access shared image.
+ * @return AccessPermissionStatus, GRANTED if the previous request is granted.
+ * NOT_GRANTED if the previous request was not granted.
+ * UNKNOWN if the request is not made yet for this call.
+ */
+ public AccessPermissionStatus getPermissionStatus(){
+ return mPermissionStatus;
+ }
+
+ /**
+ * set the permission status.
+ * @param boolean granted. true if GRANTED else false means NOT_GRANTED.
+ */
+ public void setPermissionStatus(boolean granted) {
+ mPermissionStatus = granted ? AccessPermissionStatus.GRANTED
+ : AccessPermissionStatus.NOT_GRANTED;
+ }
+
+ /**
+ * get the location image byte array
+ * @return byte array, null if image is null.
+ */
+ public byte[] getLocationImageArray() {
+ return mLocationImageArray;
+ }
+
+}
diff --git a/InCallUI/src/com/android/incallui/RcsCallPresenter.java b/InCallUI/src/com/android/incallui/RcsCallPresenter.java
new file mode 100644
index 0000000..ffb85e1
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/RcsCallPresenter.java
@@ -0,0 +1,602 @@
+/**
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.incallui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.telecom.Call.Details;
+import android.util.TypedValue;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.android.incallui.InCallPresenter.IncomingCallListener;
+
+import java.util.Locale;
+
+import org.codeaurora.rcscommon.CallComposerData;
+import org.codeaurora.rcscommon.EnrichedCallState;
+import org.codeaurora.rcscommon.RcsManager;
+
+/**
+ * RcsCallPresenter is a presenter to handle the RcsCallFragement. all rcs relatred
+ * view will be handled with RcsCallPresenter.
+ */
+public class RcsCallPresenter extends Presenter<RcsCallPresenter.RcsCallUi>
+ implements InCallStateListener, IncomingCallListener,
+ InCallDetailsListener, InCallPresenter.InCallEventListener {
+
+ public interface RcsCallUi extends Ui {
+ void updateCallComposerData(RcsCallInfo info);
+ Context getContext();
+ int getMapImageViewWidth();
+ int getMapImageViewHeight();
+ void setEnabled(boolean enabled);
+ void setVisible(boolean visible);
+ void showSmallView(boolean showSmall);
+ void showEnrichCallFailed(CallComposerData.PRIORITY priority);
+ void showEnrichCallDetail();
+ void hideEnrichCallDetail();
+ boolean hasReadStoragePermission();
+ void requestReadStoragePermission();
+ boolean isPermissionPending();
+ boolean isInForground();
+ }
+
+ public interface ImageBitmapListener {
+ void onFetchBitmap(Bitmap bitmap);
+ }
+
+ private static final int REQUEST_MAP_IMAGE = 1;
+ private static final int REQUEST_SHARED_IMAGE = 2;
+ private CallComposerData mCallComposerData = null;
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case REQUEST_MAP_IMAGE:
+ Call call = (Call) msg.obj;
+ requestRcsLocationImage(call);
+ break;
+ case REQUEST_SHARED_IMAGE:
+ call = (Call) msg.obj;
+ requestRcsSharedImage(call);
+ break;
+ }
+ }
+ };
+
+ private RcsCallHandler mRcsCallHandler = RcsCallHandler.getInstance();
+
+ public RcsCallPresenter() {
+ Log.d(this, "constructor");
+ }
+
+ @Override
+ public void onUiReady(RcsCallUi ui) {
+ Log.d(this, "onUiReady");
+ super.onUiReady(ui);
+ final InCallActivity activity = InCallPresenter.getInstance()
+ .getActivity();
+ InCallPresenter.getInstance().addListener(this);
+ InCallPresenter.getInstance().addIncomingCallListener(this);
+ InCallPresenter.getInstance().addDetailsListener(this);
+ InCallPresenter.getInstance().addInCallEventListener(this);
+ onStateChange(InCallState.NO_CALLS, InCallPresenter.getInstance()
+ .getInCallState(), CallList.getInstance());
+
+ }
+
+ @Override
+ public void onUiUnready(RcsCallUi ui) {
+ Log.d(this, "onUiUnready");
+ super.onUiUnready(ui);
+ InCallPresenter.getInstance().removeListener(this);
+ InCallPresenter.getInstance().removeIncomingCallListener(this);
+ InCallPresenter.getInstance().removeDetailsListener(this);
+ InCallPresenter.getInstance().removeInCallEventListener(this);
+ mCallComposerData = null;
+ }
+
+ @Override
+ public void onStateChange(InCallState oldState, InCallState newState,
+ CallList callList) {
+ Log.d(this, "onStateChange");
+ updateRichCallView(getCurrentCall());
+ }
+
+ private Call getCurrentCall() {
+ Call call = null;
+ CallList callList = CallList.getInstance();
+ InCallState newState = InCallPresenter.getInstance().getInCallState();
+ if (newState == InCallState.INCOMING) {
+ call = callList.getIncomingCall();
+ } else if (newState == InCallState.PENDING_OUTGOING
+ || newState == InCallState.OUTGOING) {
+ call = callList.getOutgoingCall();
+ if (call == null) {
+ call = callList.getPendingOutgoingCall();
+ }
+ } else if (newState == InCallState.INCALL) {
+ call = getCallToDisplay(callList);
+ }
+ return call;
+ }
+
+ private void updateRichCallView(Call call) {
+ Log.d(this ,"updateRichCallView");
+ RcsCallUi ui = getUi();
+ if (call != null && ui != null) {
+
+ RcsCallInfo callInfo = getRcsCallInfo(call);
+ CallComposerData data = callInfo != null ? callInfo.getCallComposerData() : null;
+ //Just for debugging purpose
+ Log.d(this ,"call state : " + call.getState());
+ Log.d(this ,"data : " + data);
+ Log.d(this ,"mCallComposerData : " + mCallComposerData);
+
+ /*
+ * Hide the RCS UI for conference call or for video call
+ */
+ if (call.isConferenceCall() || call.isVideoCall(ui.getContext())) {
+ Log.d(this," in conf call or VT call ");
+ ui.setVisible(false);
+ mCallComposerData = null;
+ return;
+ }
+
+ /*
+ * Update rcs content on UI only if we are updating for the first time
+ * and the RCS content is valid or current RCSContent should not be null
+ * and should not be same as previous. And new RCS content should be valid.
+ */
+ if ((mCallComposerData == null && data != null && data.isValid())
+ || (mCallComposerData != null && !mCallComposerData.equals(data)
+ && (data != null && data.isValid()))) {
+ /*
+ * If RCS call state is falied then hide the UI
+ * and show a enrich call failed toast message, else show the UI.
+ */
+ if (data.getCallState() == EnrichedCallState.FAILED) {
+ Log.d(this,"Enriched call failed");
+ ui.setVisible(false);
+ } else {
+ Log.d(this,"Setting visible");
+ ui.setVisible(true);
+ }
+
+ /*
+ * For a incoming call or waiting call show a small RCS view.
+ * and disable the RCS view.
+ *
+ * Show enabled and show full view in case of non INCOMING/WAITING call.
+ */
+ if(call.getState() == Call.State.INCOMING
+ || call.getState() == Call.State.CALL_WAITING) {
+ ui.setEnabled(false);
+ ui.showSmallView(true);
+ } else {
+ ui.setEnabled(true);
+ ui.showSmallView(false);
+ }
+ updateUi(call);
+ } else if ((data != null && !data.isValid()) || data == null) {
+ /*
+ * if new data is not valid or if new data is null then hide the UI
+ */
+ Log.d(this,"content not valid");
+ ui.setVisible(false);
+ /*
+ * show the enrich call failed message only once when moving state from
+ * mCallComposerData null to new callcomposerdata call state is FAILED
+ * or from valid mCallComposerData to new callcomposerdata call state is FAILED
+ */
+ if ((mCallComposerData == null && data != null
+ && data.getCallState() == EnrichedCallState.FAILED)
+ || (mCallComposerData != null
+ && mCallComposerData.getCallState() != EnrichedCallState.FAILED
+ && data != null && data.getCallState() == EnrichedCallState.FAILED)) {
+ Log.d(this, "show enrich call failed toast message");
+ ui.showEnrichCallFailed(mCallComposerData.getPriority());
+ }
+ mCallComposerData = data;
+ }
+
+ /*
+ * For a incoming call or waiting call show a small RCS view.
+ * and disable the RCS view.
+ */
+ if (call.getState() != Call.State.CALL_WAITING
+ && call.getState() != Call.State.INCOMING) {
+ ui.showSmallView(false);
+ ui.showEnrichCallDetail();
+ } else {
+ ui.showSmallView(true);
+ }
+
+ /*
+ * Request to get the Shared Image or Location Image for RCSService.
+ */
+ if (mCallComposerData != null && mCallComposerData.isValid()) {
+ boolean hasMapImgMsg = mHandler.hasMessages(REQUEST_MAP_IMAGE);
+ boolean hasShardImgMsg = mHandler.hasMessages(REQUEST_SHARED_IMAGE);
+
+ boolean canReqSharedImg = canRequestSharedImage(call);
+ boolean canReqLocImg = canRequestLocImage(call);
+ Log.d(this, "has REQUEST_MAP_IMAGE msg : " + hasMapImgMsg
+ + " canReqLocImg : " + canReqLocImg);
+ Log.d(this, "has REQUEST_SHARED_IMAGE msg : " + hasShardImgMsg
+ + " canReqSharedImg : " + canReqSharedImg);
+ if(!hasMapImgMsg && canReqLocImg) {
+ Message msg = new Message();
+ msg.obj = call;
+ msg.what = REQUEST_MAP_IMAGE;
+ mHandler.sendMessage(msg);
+ }
+ if(!hasShardImgMsg && canReqSharedImg) {
+ Message msg = new Message();
+ msg = new Message();
+ msg.obj = call;
+ msg.what = REQUEST_SHARED_IMAGE;
+ mHandler.sendMessage(msg);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDetailsChanged(Call call, android.telecom.Call.Details
+ details) {
+ Log.d(this, "onDetailsChanged");
+ updateRichCallView(getCurrentCall());
+ }
+
+ @Override
+ public void onIncomingCall(InCallState oldState, InCallState newState,
+ Call call) {
+ Log.d(this, "onIncomingCall");
+ updateRichCallView(getCurrentCall());
+ }
+
+ /**
+ * update the UI according to call.
+ * If call is RCS call then RCS content will be updated accordingly on the UI.
+ * If the call is non RCS call then no RCS content will be displayed.
+ */
+ private void updateUi(Call call) {
+ Log.d(this ,"updateUi");
+ if (call == null) {
+ Log.d(this ,"cant update call is null");
+ return;
+ }
+ RcsCallInfo callInfo = getRcsCallInfo(call) ;
+ CallComposerData data = callInfo != null ? callInfo.getCallComposerData() : null;
+ if(getUi() == null || data == null) {
+ Log.d(this ,"UI is null or data is null");
+ return;
+ }
+ getUi().updateCallComposerData(callInfo);
+ mCallComposerData = data;
+ }
+
+ protected void forceUpdateUi(){
+ Log.d(this ,"forceUpdateUi");
+ updateUi(getCurrentCall());
+ }
+
+ /**
+ * request to get the rcs shared image for a call, if available.
+ *
+ * @param Call
+ */
+ public void requestRcsSharedImage(Call call) {
+ Log.d(this, "requestRcsSharedImage : call: ");
+ RcsCallInfo callInfo = getRcsCallInfo(call) ;
+ if (call == null || callInfo == null || callInfo.getCallComposerData() == null) {
+ Log.d(this, " call or rcscallhelper or enrichcalldata is null");
+ return;
+ }
+
+ CallComposerData data = callInfo.getCallComposerData();
+ if (data != null && data.isValidSharedImageUri() && getUi() != null) {
+ Log.d(this, "requesting for permission ");
+ getUi().requestReadStoragePermission();
+ } else {
+ Log.d(this, "not a valid sharedimage or shared image is already downloaded"
+ + "or UI is null, hence ignoring");
+ }
+ }
+
+ /**
+ * request to get the rcs location image for a call, if available.
+ *
+ * @param Call
+ */
+ public void requestRcsLocationImage(Call call) {
+ Log.d(this, "requestRcsLocationImage : call: " + call);
+ int width = getUi() != null ? getUi().getMapImageViewWidth() : 0;
+ int height = getUi() != null ? getUi().getMapImageViewHeight() : 0;
+ Log.d(this, "requestRcsLocationImage : width : " + width
+ + " height: " + height);
+
+ RcsCallInfo callInfo = getRcsCallInfo(call) ;
+ if (call == null || callInfo == null || callInfo.getCallComposerData() == null) {
+ Log.d(this, " call or rcscallhelper or enrichcalldata is null");
+ return;
+ }
+ CallComposerData data = callInfo.getCallComposerData();
+ if (data != null && data.isValidLocation() && (callInfo.getLocationImageArray() == null
+ || callInfo.getLocationImageArray().length == 0)
+ && width != 0 && height != 0 && getUi() != null) {
+
+ Log.d(this, "calling requestRcsLocationImage ");
+
+ callInfo.requestRcsLocationImage(width, height,
+ getUi().getContext());
+ } else {
+ Log.d(this, "not a valid location or location image is already download"
+ + " or view width/height is 0 or UI is null, hence ignoring");
+ }
+ }
+
+ /**
+ * onSharedImageClicked will be called from the RcsCallFragment when shared image view
+ * is clicked.
+ *
+ * @param uri of the image
+ */
+ public void onSharedImageClicked(Uri uri) {
+ Log.d(this,"onSharedImageClicked : " + uri);
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_VIEW);
+ if(uri != null && !uri.toString().startsWith("file")) {
+ uri = Uri.parse("file://" + uri.getSchemeSpecificPart());
+ }
+ intent.setDataAndType(uri, "image/*");
+ if (intent.resolveActivity(getUi().getContext().getPackageManager()) != null) {
+ getUi().getContext().startActivity(intent);
+ } else {
+ QtiCallUtils.displayToast(getUi().getContext(),
+ R.string.can_not_launch_image_application);
+ }
+ }
+
+ /**
+ * onMapImageClicked will be called from the RcsCallFragment when Location image view
+ * is clicked.
+ *
+ * @param double latitude
+ * @param double longitude
+ */
+ public void onMapImageClicked(double latitude, double longitude) {
+ String uri = String.format(Locale.ENGLISH, "geo:%f,%f?q=%f,%f", latitude,
+ longitude, latitude, longitude);
+ Log.d(this,"GEO : " + uri);
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
+ if (intent.resolveActivity(getUi().getContext().getPackageManager()) != null) {
+ getUi().getContext().startActivity(intent);
+ } else {
+ QtiCallUtils.displayToast(getUi().getContext(),
+ R.string.can_not_launch_maps_application);
+ }
+ }
+
+ /**
+ * onReadStoragePermission will be called from the RcsCallFragment when a read storage access
+ * permission is either accepted or rejected.
+ *
+ * @param boolean granted. true if accepted else false.
+ */
+ public void onReadStoragePermission(boolean granted) {
+ Log.d(this,"onReadStoragePermission : " + granted);
+ Call call = getCurrentCall();
+ if (call != null) {
+ RcsCallInfo callInfo = mRcsCallHandler.getRcsCallInfo(call);
+ if(callInfo != null) {
+ callInfo.setPermissionStatus(granted);
+ }
+ }
+ if (granted) {
+ updateUi(call);
+ }
+ }
+
+ /**
+ * Get the highest priority call to display.
+ * Goes through the calls and chooses which to return based on priority of which type of call
+ * to display to the user.
+ *
+ * @param CallList
+ */
+ private Call getCallToDisplay(CallList callList) {
+ // Active calls come second. An active call always gets precedent.
+ Call retval = callList.getActiveCall();
+ if (retval != null) {
+ return retval;
+ }
+ retval = callList.getDisconnectingCall();
+ if (retval != null) {
+ return retval;
+ }
+ retval = callList.getDisconnectedCall();
+ if (retval != null) {
+ return retval;
+ }
+ // Then we go to background call.
+ retval = callList.getBackgroundCall();
+ if (retval != null) {
+ return retval;
+ }
+
+ // At last, get second background call.
+ retval = callList.getSecondBackgroundCall();
+
+ return retval;
+ }
+
+ /**
+ * This function is used to check if we can request to get shared image.
+ *
+ * @return true if there is a valid callcomposerdata attached to call and it is valid
+ * and if the callcomposerdata has the valid shared image uri/path
+ * and application has the read storage permission
+ * and there are no pending request get read storage permission request. Else returns false.
+ *
+ * Note: this function returns false if application is not having read external storage
+ * permission and the same time call state is INCOMING or CALL_WAITING
+ */
+ private boolean canRequestSharedImage(Call call) {
+ boolean incomingCall = call != null && (call.getState() == Call.State.INCOMING
+ || call.getState() == Call.State.CALL_WAITING);
+
+ RcsCallInfo callInfo = mRcsCallHandler.getRcsCallInfo(call);
+ CallComposerData data = callInfo.getCallComposerData();
+
+ return data != null && data.isValid()
+ && data.isValidSharedImageUri() && call != null
+ && (!getUi().hasReadStoragePermission() ? !incomingCall : true)
+ && !getUi().isPermissionPending()
+ && callInfo != null
+ && callInfo.getPermissionStatus() != RcsCallInfo.AccessPermissionStatus.NOT_GRANTED
+ && getUi().isInForground();
+ }
+
+ /**
+ * This function is used to check if we can request to get location image.
+ *
+ * @return true if there is a valid callcomposerdata attached to call and it is valid
+ * and if the callcomposerdata has the valid location coordinates
+ * and there are no pending request to fetch location image
+ * and if the location image is not downloaded yet. Else returns false.
+ */
+ private boolean canRequestLocImage(Call call) {
+ RcsCallInfo callInfo = getRcsCallInfo(call);
+ CallComposerData data = callInfo.getCallComposerData();
+ return data != null && data.isValid()
+ && call != null && callInfo != null
+ && !callInfo.isLocImageReqested()
+ && data.isValidLocation()
+ && (callInfo.getLocationImageArray() == null
+ || callInfo.getLocationImageArray().length == 0);
+ }
+
+ /**
+ * getRcsCallInfo function will provide the RcsCallInfo for a
+ * perticular call.
+ *
+ * @return RcsCallInfo, null if RcsCallInfo did not find.
+ */
+ private RcsCallInfo getRcsCallInfo(Call call) {
+ return mRcsCallHandler.getRcsCallInfo(call);
+ }
+
+ /**
+ * updateImageFromUri function will be called from RcsCallFragment to get the bitmap
+ * form a Uri to display the bitmap in an image view.
+ *
+ * @param Uri, image uri
+ * @param ImageBitmapListener callback listener to get the bitmap object
+ */
+ public void updateImageFromUri(final Uri uri, final ImageBitmapListener listener) {
+ Log.d(this, "updateImageFromBytes ");
+ AsyncTask<Void, Void, Bitmap> task = new AsyncTask<Void, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(Void... params) {
+ return BitmapFactory.decodeFile(uri.getSchemeSpecificPart());
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap image) {
+ listener.onFetchBitmap(image);
+ }
+ };
+ task.execute(null, null, null);
+ }
+
+ /**
+ * updateImageFromBytes function will be called from RcsCallFragment to get the bitmap
+ * form a byte array to display the bitmap in an image view.
+ *
+ * @param byte[], image bytes
+ * @param ImageBitmapListener callback listener to get the bitmap object
+ */
+ public void updateImageFromBytes(final byte[] arr, final ImageBitmapListener listener) {
+ Log.d(this, "updateImageFromBytes ");
+ AsyncTask<Void, Void, Bitmap> task = new AsyncTask<Void, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(Void... params) {
+ return BitmapFactory.decodeByteArray(arr, 0, arr.length);
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap image) {
+ listener.onFetchBitmap(image);
+ }
+ };
+ task.execute(null, null, null);
+ }
+
+ @Override
+ public void onFullscreenModeChanged(boolean isFullscreenMode) {
+ }
+
+ @Override
+ public void onSecondaryCallerInfoVisibilityChanged(boolean isVisible, int height) {
+ }
+
+ @Override
+ public void onIncomingVideoAvailabilityChanged(boolean isAvailable) {
+ }
+
+ @Override
+ public void onSendStaticImageStateChanged(boolean isEnabled) {
+ }
+
+ @Override
+ public void onAnswerViewGrab(boolean isGrabbed) {
+ Log.d(this, "onAnswerViewGrab : " + isGrabbed);
+ if(isGrabbed) {
+ getUi().hideEnrichCallDetail();
+ } else {
+ getUi().showEnrichCallDetail();
+ }
+ }
+
+ @Override
+ public void updatePrimaryCallState() {
+ }
+
+}
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 1efa3cc..7bd35a3 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -399,8 +399,16 @@
builder.setCategory(Notification.CATEGORY_CALL);
}
- // Set the content
- builder.setContentText(content);
+ // if enrich call and urgent the highlight text
+ // in red
+ if (InCallRcsUtils.isEnrichCall(call, mContext)) {
+ builder.setContentText(InCallRcsUtils.getEnrichContentText(
+ mContext, call, content));
+ } else {
+ // Set the content
+ builder.setContentText(content);
+ }
+
builder.setSmallIcon(iconResId);
builder.setContentTitle(contentTitle);
builder.setLargeIcon(largeIcon);
@@ -614,7 +622,11 @@
if (supportsVoicePrivacy) {
resId = R.drawable.stat_sys_vp_phone_call;
} else {
- resId = R.drawable.ic_call_white_24dp;
+ if (InCallRcsUtils.isEnrichCall(call, mContext)) {
+ resId = R.drawable.ic_enrich_call_white_24dp;
+ } else {
+ resId = R.drawable.ic_call_white_24dp;
+ }
}
}
return resId;
@@ -642,6 +654,12 @@
resId = R.string.notification_ongoing_call_wifi;
}
+ if (InCallRcsUtils.isEnrichCall(call, mContext)) {
+ resId = InCallRcsUtils.getEnrichContentString(call,
+ isIncomingOrWaiting);
+ return mContext.getString(resId);
+ }
+
if (isIncomingOrWaiting) {
if (call.isIncomingConfCall()) {
resId = R.string.notification_incoming_conf_call;
diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
index 92bd5e5..c3e7f38 100644
--- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
@@ -591,6 +591,11 @@
}
@Override
+ public void onAnswerViewGrab(boolean isGrabbed) {
+ // No-op - required for RcsCallPresenter.
+ }
+
+ @Override
public void onIncomingVideoAvailabilityChanged(boolean isAvailable) {
//NO OP
}
diff --git a/res/drawable-xxxhdpi/ic_dialer_sip_white_24dp.png b/res/drawable-xxxhdpi/ic_dialer_sip_white_24dp.png
new file mode 100644
index 0000000..936dd6c
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_dialer_sip_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_rcs_call_black_24dp.png b/res/drawable-xxxhdpi/ic_rcs_call_black_24dp.png
new file mode 100644
index 0000000..313d9ad
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_rcs_call_black_24dp.png
Binary files differ
diff --git a/res/layout/call_log_list_item_action_enriched_call.xml b/res/layout/call_log_list_item_action_enriched_call.xml
new file mode 100755
index 0000000..32f0512
--- /dev/null
+++ b/res/layout/call_log_list_item_action_enriched_call.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/rich_call_action"
+ style="@style/CallLogActionStyle"
+ android:visibility="gone" >
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <ProgressBar
+ android:id="@+id/richfetchprogressBar"
+ style="@style/CallLogActionIconStyle"
+ android:indeterminate="true"
+ android:indeterminateTint="?attr/call_log_secondary_text_color" />
+
+ <ImageView
+ android:id="@+id/richCallIcon"
+ style="@style/CallLogActionIconStyle"
+ android:alpha="0.3"
+ android:src="@drawable/ic_rcs_call_black_24dp"
+ android:visibility="gone" />
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/rich_call_action_text"
+ style="@style/CallLogActionTextStyle"
+ android:alpha="0.3"
+ android:text="@string/call_log_action_rich_call" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/call_log_list_item_actions.xml b/res/layout/call_log_list_item_actions.xml
index 4cce073..71e4c43 100644
--- a/res/layout/call_log_list_item_actions.xml
+++ b/res/layout/call_log_list_item_actions.xml
@@ -59,6 +59,8 @@
</LinearLayout>
+ <include layout="@layout/call_log_list_item_action_enriched_call" />
+
<LinearLayout
android:id="@+id/video_call_action"
style="@style/CallLogActionStyle">
diff --git a/res/layout/shortcut_list_item_rich_call_enabled.xml b/res/layout/shortcut_list_item_rich_call_enabled.xml
new file mode 100755
index 0000000..b8fd899
--- /dev/null
+++ b/res/layout/shortcut_list_item_rich_call_enabled.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:custom="http://schemas.android.com/apk/res/com.android.dialer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:baselineAligned="false"
+ android:gravity="center_vertical"
+ android:orientation="horizontal" >
+ <FrameLayout
+ android:id="@+id/rich_call_search_group"
+ android:layout_width="@dimen/enriched_rich_call_search_group_width"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+ <ProgressBar
+ android:id="@+id/richfetchserachprogressBar"
+ android:layout_width="@dimen/call_log_action_icon_dimen"
+ android:layout_height="@dimen/call_log_action_icon_dimen"
+ android:visibility="gone"
+ android:layout_marginLeft="@dimen/enriched_progressbar_margin_left"
+ android:layout_marginTop="@dimen/enriched_progressbar_margin_top"
+ android:indeterminate="true" />
+ <com.android.contacts.common.list.ContactListItemView
+ android:id="@+id/shortcut_rich_call"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ custom:list_item_gap_between_image_and_text="@dimen/enriched_list_item_gap_between_image_and_text"
+ custom:list_item_padding_right="@dimen/enriched_list_item_padding_right"/>
+ </FrameLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d9c425d..0f9370a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -178,4 +178,16 @@
<dimen name="list_all_calls">200dp</dimen>
<dimen name="empty_list_message_text_size_for_Marshmallow">16dp</dimen>
<dimen name="call_log_spinner_text_size">16sp</dimen>
+
+ <!-- Enriched call integration dimensions -->
+ <dimen name="enriched_progressbar_margin_left">24dp</dimen>
+ <dimen name="enriched_progressbar_margin_top">14dp</dimen>
+ <dimen name="enriched_list_item_gap_between_image_and_text">14dp</dimen>
+ <dimen name="enriched_list_item_padding_right">2dp</dimen>
+ <dimen name="enriched_video_list_item_gap_between_image_and_text">1dp</dimen>
+ <dimen name="enriched_video_list_item_padding_left">0dp</dimen>
+ <dimen name="enriched_video_list_item_padding_right">5dp</dimen>
+ <dimen name="enriched_rich_call_search_group_width">0dp</dimen>
+ <dimen name="enriched_shortcut_video_call_width">0dp</dimen>
+
</resources>
diff --git a/res/values/qtistrings.xml b/res/values/qtistrings.xml
index 73fbf5c..bde2e3a 100644
--- a/res/values/qtistrings.xml
+++ b/res/values/qtistrings.xml
@@ -35,4 +35,14 @@
<!-- Call Settings -->
<string name="call_settings_lbl">Call Settings</string>
+
+ <!-- RCS feature strings -->
+ <string name="type_incoming_rcs">Incoming Rich Call</string>
+ <string name="type_outgoing_rcs">Outgoing Rich Call</string>
+ <string name="type_missed_rcs">Missed Rich Call</string>
+ <string name="type_declined_rcs">Declined Rich Call</string>
+ <string name="rcs_service_unavailable">Rich call service is currently
+ unavailable, dialing voice call</string>
+ <!-- Rich call feature integration call log details -->
+ <string name="call_log_action_rich_call">Make Rich Call</string>
</resources>
diff --git a/src/com/android/dialer/DialerApplication.java b/src/com/android/dialer/DialerApplication.java
index 1a0497b..0ed1ebc 100644
--- a/src/com/android/dialer/DialerApplication.java
+++ b/src/com/android/dialer/DialerApplication.java
@@ -43,6 +43,7 @@
Trace.endSection();
new BlockedNumbersAutoMigrator(PreferenceManager.getDefaultSharedPreferences(this),
new FilteredNumberAsyncQueryHandler(getContentResolver())).autoMigrate();
+ EnrichedCallHandler.init(this);
Trace.endSection();
}
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 08cb8cb..68c51c6 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -51,6 +51,7 @@
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import android.view.inputmethod.EditorInfo;
import android.widget.AbsListView.OnScrollListener;
import android.widget.EditText;
import android.widget.ImageButton;
@@ -72,6 +73,7 @@
import com.android.dialer.dialpad.SmartDialNameMatcher;
import com.android.dialer.dialpad.SmartDialPrefix;
import com.android.dialer.interactions.PhoneNumberInteraction;
+import com.android.dialer.list.DialerPhoneNumberListAdapter;
import com.android.dialer.list.DragDropController;
import com.android.dialer.list.ListsFragment;
import com.android.dialer.list.OnDragDropListener;
@@ -263,6 +265,8 @@
*/
private String mVoiceSearchQuery;
+ private EnrichedCallHandler mEnrichedCallHandler = null;
+
protected class OptionsPopupMenu extends PopupMenu {
public OptionsPopupMenu(Context context, View anchor) {
super(context, anchor, Gravity.END);
@@ -367,6 +371,23 @@
};
/**
+ * Make changes to rich call button when the search view has focus
+ */
+ private final View.OnClickListener mSearchViewClickListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mEnrichedCallHandler.isRcsFeatureEnabled()) {
+ EnrichedCallHandler.DialtactsActivityListener listener =
+ getSearchListAdapterHelper();
+ if (listener != null) {
+ listener.onShowDialpadFragment();
+ }
+ }
+ }
+ };
+
+ /**
* Handles the user closing the soft keyboard.
*/
private final View.OnKeyListener mSearchEditTextLayoutListener = new View.OnKeyListener() {
@@ -380,11 +401,22 @@
// If the search term is not empty, show the dialpad fab.
showFabInSearchUi();
}
+ onHideDialpadFragment();
}
return false;
}
};
+ private void onHideDialpadFragment() {
+ if (mEnrichedCallHandler.isRcsFeatureEnabled()) {
+ EnrichedCallHandler.DialtactsActivityListener listener =
+ getSearchListAdapterHelper();
+ if (listener != null) {
+ listener.onHideDialpadFragment();
+ }
+ }
+ }
+
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -419,8 +451,19 @@
searchEditTextLayout.setPreImeKeyListener(mSearchEditTextLayoutListener);
mActionBarController = new ActionBarController(this, searchEditTextLayout);
-
+ mEnrichedCallHandler = EnrichedCallHandler.getInstance();
mSearchView = (EditText) searchEditTextLayout.findViewById(R.id.search_view);
+ mSearchView.setOnClickListener(mSearchViewClickListener);
+ mSearchView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ onHideDialpadFragment();
+ }
+ return false;
+ }
+ });
mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
mVoiceSearchButton = searchEditTextLayout.findViewById(R.id.voice_search_button);
searchEditTextLayout.findViewById(R.id.search_magnifying_glass)
@@ -818,6 +861,14 @@
* @see #onDialpadShown
*/
private void showDialpadFragment(boolean animate) {
+ if (mEnrichedCallHandler.isRcsFeatureEnabled()) {
+ EnrichedCallHandler.DialtactsActivityListener listener =
+ getSearchListAdapterHelper();
+ if (listener != null) {
+ listener.onShowDialpadFragment();
+ }
+ }
+
if (mIsDialpadShown || mStateSaved) {
return;
}
@@ -879,6 +930,9 @@
* @see #commitDialpadFragmentHide
*/
public void hideDialpadFragment(boolean animate, boolean clearDialpad) {
+ if (mIsDialpadShown) {
+ onHideDialpadFragment();
+ }
if (mDialpadFragment == null || mDialpadFragment.getView() == null) {
return;
}
@@ -1311,9 +1365,36 @@
return false;
}
+ private EnrichedCallHandler.DialtactsActivityListener
+ getSearchListAdapterHelper() {
+ DialerPhoneNumberListAdapter adapter = null;
+ if (mSmartDialSearchFragment != null
+ && mSmartDialSearchFragment.isVisible()) {
+ adapter = (DialerPhoneNumberListAdapter)
+ mSmartDialSearchFragment.getAdapter();
+ } else if (mRegularSearchFragment != null
+ && mRegularSearchFragment.isVisible()) {
+ adapter = (DialerPhoneNumberListAdapter)
+ mRegularSearchFragment.getAdapter();
+ }
+ if (adapter != null) {
+ return (EnrichedCallHandler.DialtactsActivityListener)
+ adapter.getEnrichedHelper();
+ }
+ return null;
+ }
+
@Override
public void onListFragmentScrollStateChange(int scrollState) {
if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
+ if (mEnrichedCallHandler.isRcsFeatureEnabled()
+ && mSearchView.hasFocus()) {
+ EnrichedCallHandler.DialtactsActivityListener listener =
+ getSearchListAdapterHelper();
+ if (listener != null) {
+ listener.onListFragmentScrollStateChange();
+ }
+ }
hideDialpadFragment(true, false);
DialerUtils.hideInputMethod(mParentLayout);
}
diff --git a/src/com/android/dialer/EnrichedCallHandler.java b/src/com/android/dialer/EnrichedCallHandler.java
new file mode 100755
index 0000000..5a8f4e0
--- /dev/null
+++ b/src/com/android/dialer/EnrichedCallHandler.java
@@ -0,0 +1,171 @@
+/**
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.dialer;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.CallLog.Calls;
+import android.telephony.SubscriptionManager;
+
+import com.android.dialer.util.AppCompatConstants;
+import org.codeaurora.rcscommon.RcsManager;
+
+/**
+ * A singleton class which manages the use of RCSManager
+ */
+public class EnrichedCallHandler {
+
+ /**
+ * Interface to be implemented by the search helper
+ */
+ public interface DialtactsActivityListener {
+ void onListFragmentScrollStateChange();
+ void onHideDialpadFragment();
+ void onShowDialpadFragment();
+ }
+
+ private RcsManager mRcsManager;
+ private boolean mIsRcsFeatureEnabled = false;
+ private static EnrichedCallHandler sEnrichedCallHandler = null;
+
+ /** Name used to identify RCS incoming calls. */
+ private final CharSequence mIncomingRcsName;
+ /** Name used to identify RCS outgoing calls. */
+ private final CharSequence mOutgoingRcsName;
+ /** Name used to identify missed calls. */
+ private final CharSequence mMissedName;
+ /** Name used to identify missed RCS incoming calls. */
+ private final CharSequence mMissedRcsName;
+ /** Name used to identify declined RCS incoming calls. */
+ private final CharSequence mDeclinedRcsName;
+
+ // Messages for RCS capability checks
+ public static final int MSG_ENABLE_RCS = 100;
+ public static final int MSG_DISABLE_RCS = 101;
+ public static final int MSG_CHECK_RCS = 102;
+ public static final int MSG_ENABLE_RCS_CHECK_PROGRESS = 103;
+ public static final int MSG_DISABLE_RCS_CHECK_PROGRESS = 104;
+ public static final int MSG_NETWORK_FAILURE = 105;
+ public static final int RCS_CHECK_TIMEOUT = 32*1000;
+
+ private EnrichedCallHandler(Context context) {
+ mRcsManager = RcsManager.getInstance(context);
+ mIsRcsFeatureEnabled = mRcsManager.isEnrichCallFeatureEnabled();
+ if (mIsRcsFeatureEnabled) {
+ mRcsManager.initialize();
+ }
+ // Cache these values so that we do not need to look them up each time.
+ mIncomingRcsName = context.getString(R.string.type_incoming_rcs);
+ mOutgoingRcsName = context.getString(R.string.type_outgoing_rcs);
+ mMissedName = context.getString(R.string.type_missed);
+ mMissedRcsName = context.getString(R.string.type_missed_rcs);
+ mDeclinedRcsName = context.getString(R.string.type_declined_rcs);
+ }
+
+ /**
+ * Initialize enriched call handler
+ * @param context context
+ */
+ public static void init(Context context) {
+ if (sEnrichedCallHandler == null) {
+ sEnrichedCallHandler = new EnrichedCallHandler(context);
+ }
+ }
+
+ /**
+ * Returns the enrich call handler instance
+ * @return singleton instance
+ */
+ public static EnrichedCallHandler getInstance() {
+ return sEnrichedCallHandler;
+ }
+
+ /**
+ * Returns the reference to RCS manager
+ * @return rcsmanager
+ */
+ public RcsManager getRcsManager() {
+ return mRcsManager;
+ }
+
+ /**
+ * Returns the status of the enriched call feature
+ * @return is feature enabled
+ */
+ public boolean isRcsFeatureEnabled() {
+ return mIsRcsFeatureEnabled;
+ }
+
+ /**
+ * get the enriched call capability
+ * @return the feature capability
+ */
+ public boolean isEnrichedCallCapable() {
+ int subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ return mRcsManager.isEnrichedCallCapable(subId);
+ }
+
+ /**
+ * get the enriched call capability
+ * @return the feature capability
+ */
+ public boolean isEnrichedCallCapable(int subId) {
+ return mRcsManager.isEnrichedCallCapable(subId);
+ }
+
+ /**
+ * Initialize the RCSManager if the service is not connected
+ */
+ public void initializeRcsManager() {
+ mRcsManager.initialize();
+ }
+
+ /**
+ * Returns the text used to represent the given call type.
+ * @param int callType
+ */
+ public CharSequence getCallTypeText(int callType) {
+ switch (callType) {
+ case Calls.INCOMING_TYPE:
+ case AppCompatConstants.INCOMING_IMS_TYPE:
+ return mIncomingRcsName;
+ case Calls.OUTGOING_TYPE:
+ case AppCompatConstants.OUTGOING_IMS_TYPE:
+ return mOutgoingRcsName;
+ case Calls.MISSED_TYPE:
+ case AppCompatConstants.MISSED_IMS_TYPE:
+ return mMissedRcsName;
+ case AppCompatConstants.CALLS_REJECTED_TYPE:
+ return mDeclinedRcsName;
+ default:
+ return mMissedName;
+ }
+ }
+}
diff --git a/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java b/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java
index 08689ad..a02ae53 100644
--- a/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java
+++ b/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java
@@ -30,6 +30,7 @@
import com.android.contacts.common.CallUtil;
import com.android.dialer.PhoneCallDetails;
import com.android.dialer.R;
+import com.android.dialer.EnrichedCallHandler;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.AppCompatConstants;
import com.android.dialer.util.PresenceHelper;
@@ -120,16 +121,31 @@
+ ", callType = " + callType);
callTypeIconView.clear();
callTypeIconView.add(callType);
+
/**
- * Ims icon(VoLTE/VoWiFi/ViLTE/ViWiFi) will be shown if carrierOne is supported
- * otherwise, default video icon will be shown if it is a video call.
+ * RCS icon will be shown if its a RCS call, otherwise go for IMS icon
*/
- if (QtiImsExtUtils.isCarrierOneSupported()) {
- callTypeIconView.addImsIcon(callType, isVideoCall);
+ boolean isRcsCall = false;
+ if ((details.features & Calls.FEATURES_ENRICHED) == Calls.FEATURES_ENRICHED) {
+ isRcsCall = true;
+ callTypeIconView.setShowRcs(isRcsCall);
} else {
- callTypeIconView.setShowVideo(isVideoCall);
+ /**
+ * Ims icon(VoLTE/VoWiFi/ViLTE/ViWiFi) will be shown if carrierOne is supported
+ * otherwise, default video icon will be shown if it is a video call.
+ */
+ if (QtiImsExtUtils.isCarrierOneSupported()) {
+ callTypeIconView.addImsIcon(callType, isVideoCall);
+ } else {
+ callTypeIconView.setShowVideo(isVideoCall);
+ }
}
- callTypeTextView.setText(mCallTypeHelper.getCallTypeText(callType, isVideoCall));
+
+ CharSequence callTypeText =
+ isRcsCall ? EnrichedCallHandler.getInstance().getCallTypeText(callType) :
+ mCallTypeHelper.getCallTypeText(callType, isVideoCall);
+
+ callTypeTextView.setText(callTypeText);
// Set the date.
CharSequence dateValue = DateUtils.formatDateRange(mContext, details.date, details.date,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 96551a4..ae24b8d 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -444,7 +444,10 @@
if (!menuVisible) {
updateOnTransition();
} else if (isResumed()) {
- refreshData();
+ if (mRefreshDataRequired) {
+ refreshData();
+ mAdapter.onResume();
+ }
}
}
}
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
index 7fa4757..95def51 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
@@ -56,6 +56,7 @@
import com.android.contacts.common.util.UriUtils;
import com.android.dialer.DialtactsActivity;
import com.android.dialer.R;
+import com.android.dialer.EnrichedCallHandler;
import com.android.dialer.calllog.calllogcache.CallLogCache;
import com.android.dialer.compat.FilteredNumberCompat;
import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
@@ -238,6 +239,7 @@
private View.OnClickListener mExpandCollapseListener;
private boolean mVoicemailPrimaryActionButtonClicked;
+ private EnrichedCallLogListItemHelper mEnrichedHelper;
private CallLogListItemViewHolder(
Context context,
@@ -435,6 +437,19 @@
videoCallButtonView = actionsView.findViewById(R.id.video_call_action);
videoCallButtonView.setOnClickListener(this);
+ // rich call feature integration
+ if (EnrichedCallHandler.getInstance().isRcsFeatureEnabled()) {
+ mEnrichedHelper = new EnrichedCallLogListItemHelper(mContext);
+ mEnrichedHelper.inflateEnrichedItem((ViewGroup) actionsView);
+ mEnrichedHelper.setEnrichedGetNumberHelper(
+ new EnrichedCallLogListItemHelper.EnrichedGetNumberHelper() {
+ @Override
+ public String getNumber() {
+ return number;
+ }
+ });
+ }
+
createNewContactButtonView = actionsView.findViewById(R.id.create_new_contact_action);
createNewContactButtonView.setOnClickListener(this);
@@ -674,6 +689,11 @@
// Inflate the view stub if necessary, and wire up the event handlers.
inflateActionViewStub();
expandShowActions();
+
+ if (mEnrichedHelper != null) {
+ mEnrichedHelper.showActions();
+ }
+
actionsView.setAlpha(1.0f);
} else {
// When recycling a view, it is possible the actionsView ViewStub was previously
diff --git a/src/com/android/dialer/calllog/CallTypeIconsView.java b/src/com/android/dialer/calllog/CallTypeIconsView.java
index 7c0034c..cd353c3 100644
--- a/src/com/android/dialer/calllog/CallTypeIconsView.java
+++ b/src/com/android/dialer/calllog/CallTypeIconsView.java
@@ -29,6 +29,7 @@
import com.android.contacts.common.testing.NeededForTesting;
import com.android.contacts.common.util.BitmapUtil;
import com.android.dialer.R;
+import com.android.dialer.EnrichedCallHandler;
import com.android.dialer.util.AppCompatConstants;
import com.google.common.collect.Lists;
@@ -44,6 +45,7 @@
public class CallTypeIconsView extends View {
private List<Integer> mCallTypes = Lists.newArrayListWithCapacity(3);
private boolean mShowVideo = false;
+ private boolean mShowRcs = false;
private int mWidth;
private int mHeight;
@@ -136,6 +138,21 @@
}
/**
+ * Determines whether the RCS call icon will be shown.
+ *
+ * @param mShowRcs True where the RCS icon should be shown.
+ */
+ public void setShowRcs(boolean showRcs) {
+ mShowRcs = showRcs;
+
+ if (showRcs) {
+ mWidth += sResources.rcsCall.getIntrinsicWidth();
+ mHeight = Math.max(mHeight, sResources.rcsCall.getIntrinsicHeight());
+ invalidate();
+ }
+ }
+
+ /**
* Determines if the video icon should be shown.
*
* @return True if the video icon should be shown.
@@ -206,6 +223,15 @@
left = right + sResources.iconMargin;
}
+ // If showing the RCS call icon, draw it scaled appropriately.
+ if (mShowRcs && sResources.rcsCall!=null) {
+ final Drawable drawable = sResources.rcsCall;
+ final int right = left + drawable.getIntrinsicWidth();
+ drawable.setBounds(left, 0, right, drawable.getIntrinsicHeight());
+ drawable.draw(canvas);
+ left = right + sResources.iconMargin;
+ }
+
for (Integer callType : mCallTypes) {
final Drawable drawableIms = getLteOrWifiDrawable(callType, mShowVideo);
if (drawableIms != null) {
@@ -237,6 +263,11 @@
public final Drawable videoCall;
/**
+ * Drawable repesenting a RCS call.
+ */
+ public final Drawable rcsCall;
+
+ /**
* The margin to use for icons.
*/
public final int iconMargin;
@@ -295,6 +326,14 @@
videoCall.setColorFilter(r.getColor(R.color.dialtacts_secondary_text_color),
PorterDuff.Mode.MULTIPLY);
+ if (EnrichedCallHandler.getInstance().isRcsFeatureEnabled()) {
+ rcsCall = r.getDrawable(R.drawable.ic_dialer_sip_white_24dp).mutate();
+ rcsCall.setColorFilter(r.getColor(R.color.dialtacts_secondary_text_color),
+ PorterDuff.Mode.MULTIPLY);
+ } else {
+ rcsCall = null;
+ }
+
iconMargin = r.getDimensionPixelSize(R.dimen.call_log_icon_margin);
viwifiCall = r.getDrawable(R.drawable.viwifi);
diff --git a/src/com/android/dialer/calllog/EnrichedCallLogListItemHelper.java b/src/com/android/dialer/calllog/EnrichedCallLogListItemHelper.java
new file mode 100755
index 0000000..4f7d9c3
--- /dev/null
+++ b/src/com/android/dialer/calllog/EnrichedCallLogListItemHelper.java
@@ -0,0 +1,178 @@
+/**
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.dialer.calllog;
+
+import com.android.contacts.common.CallUtil;
+import com.android.dialer.EnrichedCallHandler;
+import com.android.dialer.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.SubscriptionManager;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import org.codeaurora.rcscommon.RcsManager;
+
+public class EnrichedCallLogListItemHelper implements View.OnClickListener {
+
+ public interface EnrichedGetNumberHelper {
+ String getNumber();
+ }
+
+ private Context mContext;
+ private View mEnrichedCallButtonView;
+ private ProgressBar mRichFetchProgressBar;
+ private TextView mRichCallText;
+ private ImageView mRichCallIcon;
+ private EnrichedGetNumberHelper mEnrichedGetNumberHelper;
+
+ public EnrichedCallLogListItemHelper(Context context) {
+ mContext = context;
+ }
+
+ public void setEnrichedGetNumberHelper(
+ EnrichedGetNumberHelper listener) {
+ this.mEnrichedGetNumberHelper = listener;
+ }
+
+ public void inflateEnrichedItem(ViewGroup root) {
+ mEnrichedCallButtonView = root.findViewById(R.id.rich_call_action);
+ mEnrichedCallButtonView.setVisibility(View.VISIBLE);
+ mRichCallText = (TextView) root
+ .findViewById(R.id.rich_call_action_text);
+ mRichCallIcon = (ImageView) root.findViewById(R.id.richCallIcon);
+ mEnrichedCallButtonView.setOnClickListener(this);
+ mEnrichedCallButtonView.setEnabled(false);
+ mRichFetchProgressBar = (ProgressBar) mEnrichedCallButtonView
+ .findViewById(R.id.richfetchprogressBar);
+ }
+
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
+ public void handleMessage(android.os.Message msg) {
+ if (msg.what == EnrichedCallHandler.MSG_ENABLE_RCS) {
+ mRichFetchProgressBar.setVisibility(View.GONE);
+ mRichCallIcon.setVisibility(View.VISIBLE);
+ mRichCallIcon.setAlpha(1.0f);
+ mRichCallText.setAlpha(1.0f);
+ mEnrichedCallButtonView.setEnabled(true);
+ } else if (msg.what == EnrichedCallHandler.MSG_DISABLE_RCS) {
+ if (mRichFetchProgressBar.getVisibility() == View.VISIBLE) {
+ mRichFetchProgressBar.setVisibility(View.GONE);
+ mRichCallIcon.setVisibility(View.VISIBLE);
+ mRichCallIcon.setAlpha(0.3f);
+ mRichCallText.setAlpha(0.3f);
+ mEnrichedCallButtonView.setEnabled(false);
+ }
+ } else if (msg.what == EnrichedCallHandler.MSG_CHECK_RCS) {
+ if (mEnrichedCallButtonView != null) {
+ mEnrichedCallButtonView.setEnabled(false);
+ mRichFetchProgressBar.setVisibility(View.VISIBLE);
+ mRichCallIcon.setVisibility(View.GONE);
+ mRichCallIcon.setAlpha(0.3f);
+ mRichCallText.setAlpha(0.3f);
+ initilizeAndPerformRcsCheck();
+ delayDisableRcsCheckTimedout();
+ }
+ }
+ }
+ };
+
+ public void showActions() {
+ if (EnrichedCallHandler.getInstance().isEnrichedCallCapable()) {
+ mHandler.obtainMessage(EnrichedCallHandler.MSG_CHECK_RCS)
+ .sendToTarget();
+ } else {
+ mHandler.obtainMessage(EnrichedCallHandler.MSG_DISABLE_RCS)
+ .sendToTarget();
+ }
+ }
+
+ private final org.codeaurora.rcscommon.RichCallCapabilitiesCallback mRcsCallback =
+ new org.codeaurora.rcscommon.RichCallCapabilitiesCallback.Stub() {
+ @Override
+ public void onRichCallCapabilitiesFetch(boolean isCapable) {
+ if (EnrichedCallHandler.getInstance().isEnrichedCallCapable()) {
+ mHandler.obtainMessage(
+ isCapable ? EnrichedCallHandler.MSG_ENABLE_RCS
+ : EnrichedCallHandler.MSG_DISABLE_RCS)
+ .sendToTarget();
+ }
+ }
+ };
+
+ private void delayDisableRcsCheckTimedout() {
+ mEnrichedCallButtonView.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mHandler.obtainMessage(EnrichedCallHandler.MSG_DISABLE_RCS)
+ .sendToTarget();
+ }
+ }, EnrichedCallHandler.RCS_CHECK_TIMEOUT);
+ }
+
+ private void initilizeAndPerformRcsCheck() {
+ EnrichedCallHandler.getInstance().initializeRcsManager();
+ EnrichedCallHandler.getInstance().getRcsManager()
+ .fetchEnrichedCallCapabilities(
+ mEnrichedGetNumberHelper.getNumber(), mRcsCallback,
+ SubscriptionManager.getDefaultVoiceSubscriptionId());
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.rich_call_action) {
+ EnrichedCallHandler.getInstance().getRcsManager()
+ .makeEnrichedCall(mEnrichedGetNumberHelper.getNumber(),
+ new org.codeaurora.rcscommon.NewCallComposerCallback.Stub() {
+ public void onNewCallComposer(
+ org.codeaurora.rcscommon.CallComposerData data) {
+ final Intent intent;
+ if (data != null && data.isValid()
+ && data.getPhoneNumber() != null) {
+ intent = CallUtil.getCallIntent(data
+ .getPhoneNumber());
+ intent.putExtra(
+ RcsManager.ENRICH_CALL_INTENT_EXTRA,
+ data);
+ } else {
+ intent = CallUtil
+ .getCallIntent(mEnrichedGetNumberHelper
+ .getNumber());
+ }
+ mContext.startActivity(intent);
+ }
+ }, SubscriptionManager.getDefaultVoiceSubscriptionId());
+ }
+ }
+}
diff --git a/src/com/android/dialer/calllog/MSimCallLogFragment.java b/src/com/android/dialer/calllog/MSimCallLogFragment.java
index 1eefe53..436145d 100644
--- a/src/com/android/dialer/calllog/MSimCallLogFragment.java
+++ b/src/com/android/dialer/calllog/MSimCallLogFragment.java
@@ -489,7 +489,10 @@
if (!menuVisible) {
updateOnTransition(false /* onEntry */);
} else if (isResumed()) {
- refreshData();
+ if (mRefreshDataRequired) {
+ refreshData();
+ mAdapter.onResume();
+ }
}
}
}
diff --git a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
index f82dbf8..1c18206 100644
--- a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
+++ b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
@@ -476,7 +476,7 @@
for (int i = 0; i < indexs.length; i = i + 2) {
int start = indexs[i];
int end = indexs[i + 1];
- if (start >= 0 && end >= 0) {
+ if (start >= 0 && end >= 0 && start < builder.length() - 1 && end < builder.length()) {
matchList.add(new SmartDialMatchPosition(start, end + 1));
} else {
Log.d(TAG, "Invalid index, start is:" + start + " end is:"
diff --git a/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java b/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java
index 7164de2..0ff6d9a 100644
--- a/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java
+++ b/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java
@@ -15,6 +15,7 @@
import com.android.contacts.common.list.ContactListItemView;
import com.android.contacts.common.list.PhoneNumberListAdapter;
import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.EnrichedCallHandler;
import com.android.dialer.R;
/**
@@ -33,16 +34,19 @@
public final static int SHORTCUT_INVALID = -1;
public final static int SHORTCUT_DIRECT_CALL = 0;
- public final static int SHORTCUT_CREATE_NEW_CONTACT = 1;
- public final static int SHORTCUT_ADD_TO_EXISTING_CONTACT = 2;
- public final static int SHORTCUT_SEND_SMS_MESSAGE = 3;
- public final static int SHORTCUT_MAKE_VIDEO_CALL = 4;
- public final static int SHORTCUT_BLOCK_NUMBER = 5;
+ public final static int SHORTCUT_MAKE_RICH_CALL = 1;
+ public final static int SHORTCUT_MAKE_VIDEO_CALL = 2;
+ public final static int SHORTCUT_CREATE_NEW_CONTACT = 3;
+ public final static int SHORTCUT_ADD_TO_EXISTING_CONTACT = 4;
+ public final static int SHORTCUT_SEND_SMS_MESSAGE = 5;
+ public final static int SHORTCUT_BLOCK_NUMBER = 6;
- public final static int SHORTCUT_COUNT = 6;
+ public final static int SHORTCUT_COUNT = 7;
private final boolean[] mShortcutEnabled = new boolean[SHORTCUT_COUNT];
+ private EnrichedSearchAdapterHelper mEnrichedHelper;
+
private final BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
private boolean mVideoCallingEnabled = false;
@@ -50,9 +54,16 @@
super(context);
mCountryIso = GeoUtil.getCurrentCountryIso(context);
+ if (EnrichedCallHandler.getInstance().isRcsFeatureEnabled()) {
+ mEnrichedHelper = new EnrichedSearchAdapterHelper(context, this);
+ }
mVideoCallingEnabled = CallUtil.isVideoEnabled(context);
}
+ public EnrichedSearchAdapterHelper getEnrichedHelper() {
+ return mEnrichedHelper;
+ }
+
@Override
public int getCount() {
return super.getCount() + getShortcutCount();
@@ -96,6 +107,10 @@
public View getView(int position, View convertView, ViewGroup parent) {
final int shortcutType = getShortcutTypeFromPosition(position);
if (shortcutType >= 0) {
+ if (mEnrichedHelper != null &&
+ shortcutType == SHORTCUT_MAKE_RICH_CALL) {
+ return mEnrichedHelper.createRichCallEntry();
+ }
if (convertView != null) {
assignShortcutToView((ContactListItemView) convertView, shortcutType);
return convertView;
@@ -115,7 +130,9 @@
Context context, int partition, Cursor cursor, int position, ViewGroup parent) {
final ContactListItemView view = super.newView(context, partition, cursor, position,
parent);
-
+ if (mEnrichedHelper != null) {
+ mEnrichedHelper.setDialButtonAttributes(view, position);
+ }
view.setSupportVideoCallIcon(mVideoCallingEnabled);
return view;
}
diff --git a/src/com/android/dialer/list/EnrichedSearchAdapterHelper.java b/src/com/android/dialer/list/EnrichedSearchAdapterHelper.java
new file mode 100755
index 0000000..ba9af62
--- /dev/null
+++ b/src/com/android/dialer/list/EnrichedSearchAdapterHelper.java
@@ -0,0 +1,376 @@
+/**
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.dialer.list;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.SubscriptionManager;
+import android.view.LayoutInflater;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.Toast;
+
+import com.android.dialer.R;
+
+import com.android.contacts.common.CallUtil;
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
+import com.android.dialer.EnrichedCallHandler;
+import com.android.incallui.Call.LogState;
+
+import org.codeaurora.rcscommon.RcsManager;
+
+/**
+ * A helper class for enriched call search list
+ * This class will help in creating a rich call entry
+ * and enabling/disabling it in the search list.
+ */
+public class EnrichedSearchAdapterHelper implements
+ EnrichedCallHandler.DialtactsActivityListener {
+
+ private Context mContext;
+ private int mButtonBGResource = 0;
+ private View mRichCallGroup;
+ private View mRichCallRootView;
+ private ContactListItemView mRichCallItem;
+ private ProgressBar mRcsCheckProgressBar;
+ private OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener;
+ private DialerPhoneNumberListAdapter mListAdapter;
+ private EnrichedSearchActionHelper mEnrichedSearchActionHelper;
+ private EnrichedCallHandler mEnrichedCallHandler;
+
+ /**
+ * Interface to be implemented by the Search list adapter
+ */
+ public interface EnrichedSearchActionHelper {
+ void processDialIntent(int position, long id);
+ boolean checkForProhibitedPhoneNumber(String number);
+ String getQueryString();
+ }
+
+ public EnrichedSearchAdapterHelper(Context context,
+ DialerPhoneNumberListAdapter adapter) {
+ mContext = context;
+ mListAdapter = adapter;
+ int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
+ TypedArray typedArray = context.obtainStyledAttributes(attrs);
+ mButtonBGResource = typedArray.getResourceId(0, 0);
+ typedArray.recycle();
+ mEnrichedCallHandler = EnrichedCallHandler.getInstance();
+ }
+
+ /**
+ * Handler to handle Enriched call capability check
+ */
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EnrichedCallHandler.MSG_ENABLE_RCS:
+ setRcsCapability(true);
+ break;
+ case EnrichedCallHandler.MSG_DISABLE_RCS:
+ setRcsCapability(false);
+ break;
+ case EnrichedCallHandler.MSG_ENABLE_RCS_CHECK_PROGRESS:
+ changeRcsCheckProgress(true);
+ break;
+ case EnrichedCallHandler.MSG_DISABLE_RCS_CHECK_PROGRESS:
+ changeRcsCheckProgress(false);
+ break;
+ case EnrichedCallHandler.MSG_NETWORK_FAILURE:
+ showFailureMessage();
+ break;
+ default:
+ // do nothing
+ }
+ }
+ };
+
+ /**
+ * set the attributes for the dial button in enriched search list
+ * @param ContactListItemView view
+ * @param int position
+ */
+ public void setDialButtonAttributes(ContactListItemView view, int position) {
+ view.setDialButton(
+ mContext.getResources().getDrawable(
+ R.drawable.ic_dialer_fork_current_call), true);
+ view.getDialButton().setBackgroundResource(mButtonBGResource);
+ view.getDialButton().setBackgroundTintList(
+ mContext.getResources().getColorStateList(
+ R.color.call_log_list_item_primary_action_icon_tint));
+ view.getDialButton().setOnClickListener(dialButtonClickListener);
+ view.getDialButton().setTag(new Integer(position));
+ }
+
+ private View.OnClickListener dialButtonClickListener = new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ int position = Integer.parseInt(v.getTag().toString());
+ mEnrichedSearchActionHelper.processDialIntent(position, -1);
+ }
+ };
+
+ /**
+ * Interface implementation set by the adapter
+ * @param DialerPhoneNumberListAdapterActionListener listener
+ */
+ public void setEnrichedSearchActionHelper(
+ EnrichedSearchActionHelper listener) {
+ mEnrichedSearchActionHelper = listener;
+ }
+
+ /**
+ * Callback implementation set the adapter
+ * @param OnPhoneNumberPickerActionListener listener
+ */
+ public void setNumberPickerListener(
+ OnPhoneNumberPickerActionListener listener) {
+ this.mPhoneNumberPickerActionListener = listener;
+ }
+
+ /**
+ * Create the rich call entry row
+ * @return enriched call view entry
+ */
+ public View createRichCallEntry() {
+ if (mRichCallRootView != null) {
+ return mRichCallRootView;
+ }
+ mRichCallRootView = (ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.shortcut_list_item_rich_call_enabled, null);
+ mRichCallGroup = mRichCallRootView
+ .findViewById(R.id.rich_call_search_group);
+ mRcsCheckProgressBar = (ProgressBar) mRichCallGroup
+ .findViewById(R.id.richfetchserachprogressBar);
+ mRichCallItem = (ContactListItemView) mRichCallRootView
+ .findViewById(R.id.shortcut_rich_call);
+ mRichCallItem.getPhotoView().setVisibility(View.VISIBLE);
+ setRcsCapability(EnrichedCallHandler.getInstance()
+ .isEnrichedCallCapable());
+ mRichCallItem.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ EnrichedCallHandler.getInstance().initializeRcsManager();
+ EnrichedCallHandler.getInstance().getRcsManager()
+ .makeEnrichedCall(mEnrichedSearchActionHelper.getQueryString(),
+ new org.codeaurora.rcscommon.NewCallComposerCallback.Stub() {
+ public void onNewCallComposer(
+ org.codeaurora.rcscommon.CallComposerData data) {
+ final Intent intent;
+ if (data != null && data.isValid()) {
+ intent = CallUtil.getCallIntent(data
+ .getPhoneNumber());
+ intent.putExtra(RcsManager.ENRICH_CALL_INTENT_EXTRA,
+ data);
+ } else {
+ mHandler.obtainMessage(EnrichedCallHandler
+ .MSG_NETWORK_FAILURE).sendToTarget();
+ intent = CallUtil.getCallIntent(
+ mEnrichedSearchActionHelper.getQueryString());
+ }
+ mContext.startActivity(intent);
+ }
+ }, SubscriptionManager.getDefaultVoiceSubscriptionId());
+ }
+ });
+ assignAttrs(mRichCallItem, R.drawable.ic_rcs_call_black_24dp,
+ R.string.call_log_action_rich_call);
+ return mRichCallRootView;
+ }
+
+ /**
+ * Assign attributes to the search list item
+ * @param ContactListItemView v
+ * @param int drawableId
+ * @param int stringId
+ */
+ private void assignAttrs(ContactListItemView v, int drawableId, int stringId) {
+ v.setDrawableResource(drawableId);
+ v.setDisplayName(mContext.getResources().getString(stringId));
+ v.setPhotoPosition(mListAdapter.getPhotoPosition());
+ v.setAdjustSelectionBoundsEnabled(false);
+ }
+
+ /**
+ * Set the RCS capability after the fetch is complete
+ * @param boolean isCapable
+ */
+ private void setRcsCapability(boolean isCapable) {
+ if (mRichCallItem != null) {
+ mRichCallItem.setVisibility(View.VISIBLE);
+ mRichCallItem.getPhotoView().setVisibility(View.VISIBLE);
+ mRcsCheckProgressBar.setVisibility(View.GONE);
+ mRichCallItem.setEnabled(isCapable);
+ mRichCallItem.setAlpha(isCapable ? 1.0f : 0.3f);
+ }
+ }
+
+ /**
+ * Disable the Enriched call capability check after the timeout
+ */
+ private void delayedDisableRcsCheckWhenTimedOut() {
+ if (mRichCallItem != null) {
+ mRichCallItem.postDelayed(mRcsDisableRunnable,
+ EnrichedCallHandler.RCS_CHECK_TIMEOUT);
+ }
+ }
+
+ private Runnable mRcsDisableRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mRichCallItem != null) {
+ if (mRcsCheckProgressBar != null
+ && mRcsCheckProgressBar.getVisibility() == View.VISIBLE) {
+ changeRcsCheckProgress(false);
+ }
+ }
+ }
+ };
+
+ /**
+ * Make changes to the Enriched call list item about the capability check
+ * status
+ * @param boolean enable
+ */
+ private void changeRcsCheckProgress(boolean enable) {
+ if (mRichCallItem != null) {
+ mRichCallItem.setVisibility(View.VISIBLE);
+ mRichCallItem.setAlpha(1.0f);
+ if (enable) {
+ mRichCallItem.getPhotoView().setVisibility(View.GONE);
+ mRcsCheckProgressBar.setVisibility(View.VISIBLE);
+ mRichCallItem.setEnabled(false);
+ delayedDisableRcsCheckWhenTimedOut();
+ } else {
+ mRichCallItem.setEnabled(true);
+ mRichCallItem.getPhotoView().setVisibility(View.VISIBLE);
+ mRcsCheckProgressBar.setVisibility(View.GONE);
+ mRichCallItem.removeCallbacks(mRcsDisableRunnable);
+ }
+ }
+ }
+
+ /**
+ * Show a toast message about RCS service unavailability, when mobile data is not present
+ */
+ private void showFailureMessage() {
+ ConnectivityManager cm = (ConnectivityManager)mContext
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ if ((activeNetwork == null) ||
+ (activeNetwork.getType() != ConnectivityManager.TYPE_MOBILE)) {
+ Toast toast = Toast.makeText(mContext.getApplicationContext(),
+ R.string.rcs_service_unavailable, Toast.LENGTH_SHORT);
+ toast.setGravity(Gravity.CENTER, 0, 0);
+ toast.show();
+ }
+ }
+
+ /**
+ * Called by the dialer activity when the search list scroll state changes
+ */
+ @Override
+ public void onListFragmentScrollStateChange() {
+ if (mEnrichedCallHandler.isEnrichedCallCapable()) {
+ mHandler.obtainMessage(
+ EnrichedCallHandler.MSG_ENABLE_RCS_CHECK_PROGRESS)
+ .sendToTarget();
+ initilizeAndPerformRcsCheck();
+ } else {
+ mHandler.obtainMessage(EnrichedCallHandler.MSG_DISABLE_RCS)
+ .sendToTarget();
+ }
+ }
+
+ /**
+ * Called by the dialer activity when the dialpad is hidden
+ */
+ @Override
+ public void onHideDialpadFragment() {
+ if (mEnrichedCallHandler.isEnrichedCallCapable()) {
+ mHandler.obtainMessage(
+ EnrichedCallHandler.MSG_ENABLE_RCS_CHECK_PROGRESS)
+ .sendToTarget();
+ initilizeAndPerformRcsCheck();
+ } else {
+ mHandler.obtainMessage(EnrichedCallHandler.MSG_DISABLE_RCS)
+ .sendToTarget();
+ }
+ }
+
+ /**
+ * Called by the dialer activity when the dialpad is shown
+ */
+ @Override
+ public void onShowDialpadFragment() {
+ if (mEnrichedCallHandler.isEnrichedCallCapable()) {
+ mHandler.obtainMessage(
+ EnrichedCallHandler.MSG_DISABLE_RCS_CHECK_PROGRESS)
+ .sendToTarget();
+ } else {
+ mHandler.obtainMessage(EnrichedCallHandler.MSG_DISABLE_RCS)
+ .sendToTarget();
+ }
+ }
+
+ private final org.codeaurora.rcscommon.RichCallCapabilitiesCallback mRcsCallback =
+ new org.codeaurora.rcscommon.RichCallCapabilitiesCallback.Stub() {
+ @Override
+ public void onRichCallCapabilitiesFetch(boolean isCapable) {
+ mHandler.obtainMessage(
+ isCapable ? EnrichedCallHandler.MSG_ENABLE_RCS
+ : EnrichedCallHandler.MSG_DISABLE_RCS)
+ .sendToTarget();
+ }
+ };
+
+ /**
+ * Do Enrich call capability check
+ */
+ private void initilizeAndPerformRcsCheck() {
+ mEnrichedCallHandler.initializeRcsManager();
+ mEnrichedCallHandler.getRcsManager().fetchEnrichedCallCapabilities(
+ mEnrichedSearchActionHelper.getQueryString(), mRcsCallback,
+ SubscriptionManager.getDefaultVoiceSubscriptionId());
+ }
+}
diff --git a/src/com/android/dialer/list/RegularSearchFragment.java b/src/com/android/dialer/list/RegularSearchFragment.java
index df18af0..83437a3 100644
--- a/src/com/android/dialer/list/RegularSearchFragment.java
+++ b/src/com/android/dialer/list/RegularSearchFragment.java
@@ -75,6 +75,10 @@
adapter.setDisplayPhotos(true);
adapter.setUseCallableUri(usesCallableUri());
adapter.setListener(this);
+ if (adapter.getEnrichedHelper() != null) {
+ adapter.getEnrichedHelper()
+ .setNumberPickerListener(getOnPhoneNumberPickerListener());
+ }
return adapter;
}
diff --git a/src/com/android/dialer/list/RegularSearchListAdapter.java b/src/com/android/dialer/list/RegularSearchListAdapter.java
index afc621c..0cebe8f 100644
--- a/src/com/android/dialer/list/RegularSearchListAdapter.java
+++ b/src/com/android/dialer/list/RegularSearchListAdapter.java
@@ -26,6 +26,7 @@
import com.android.contacts.common.list.DirectoryPartition;
import com.android.contacts.common.util.PhoneNumberHelper;
import com.android.dialer.calllog.ContactInfo;
+import com.android.dialer.EnrichedCallHandler;
import com.android.dialer.service.CachedNumberLookupService;
import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo;
@@ -106,11 +107,15 @@
protected boolean isChanged(boolean showNumberShortcuts) {
boolean changed = false;
+ changed |= setShortcutEnabled(SHORTCUT_MAKE_RICH_CALL,
+ EnrichedCallHandler.getInstance().isRcsFeatureEnabled()
+ && showNumberShortcuts);
changed |= setShortcutEnabled(SHORTCUT_DIRECT_CALL,
showNumberShortcuts || mIsQuerySipAddress);
changed |= setShortcutEnabled(SHORTCUT_SEND_SMS_MESSAGE, showNumberShortcuts);
- changed |= setShortcutEnabled(SHORTCUT_MAKE_VIDEO_CALL,
- showNumberShortcuts && CallUtil.isVideoEnabled(getContext()));
+ changed |= setShortcutEnabled(
+ SHORTCUT_MAKE_VIDEO_CALL, showNumberShortcuts
+ && CallUtil.isVideoEnabled(getContext()));
return changed;
}
diff --git a/src/com/android/dialer/list/SearchFragment.java b/src/com/android/dialer/list/SearchFragment.java
index 82395b6..251d202 100644
--- a/src/com/android/dialer/list/SearchFragment.java
+++ b/src/com/android/dialer/list/SearchFragment.java
@@ -44,6 +44,7 @@
import com.android.contacts.common.util.ViewUtil;
import com.android.dialer.R;
import com.android.dialer.dialpad.DialpadFragment.ErrorDialogFragment;
+import com.android.dialer.EnrichedCallHandler;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.IntentUtil;
import com.android.dialer.widget.EmptyContentView;
@@ -54,7 +55,8 @@
private OnListFragmentScrolledListener mActivityScrollListener;
private View.OnTouchListener mActivityOnTouchListener;
-
+ private boolean mIsRcsFeatureEnabled = EnrichedCallHandler
+ .getInstance().isRcsFeatureEnabled();
/*
* Stores the untouched user-entered string that is used to populate the add to contacts
* intent.
@@ -157,6 +159,12 @@
}
updatePosition(false /* animate */);
+ if (mIsRcsFeatureEnabled) {
+ DialerPhoneNumberListAdapter adapter = (DialerPhoneNumberListAdapter) getAdapter();
+ if (adapter.getEnrichedHelper() != null) {
+ setActionListeners(adapter.getEnrichedHelper());
+ }
+ }
}
@Override
@@ -199,6 +207,10 @@
mAddToContactNumber = addToContactNumber;
}
+ public String getAddToContactNumber(){
+ return mAddToContactNumber;
+ }
+
/**
* Return true if phone number is prohibited by a value -
* (R.string.config_prohibited_phone_number_regexp) in the config files. False otherwise.
@@ -234,6 +246,32 @@
return adapter;
}
+ private void setActionListeners(EnrichedSearchAdapterHelper helper) {
+ helper.setEnrichedSearchActionHelper(
+ new EnrichedSearchAdapterHelper.EnrichedSearchActionHelper(){
+
+ @Override
+ public void processDialIntent(int position, long id) {
+ SearchFragment.super.onItemClick(position, id);
+ }
+
+ @Override
+ public boolean checkForProhibitedPhoneNumber(String number) {
+ return SearchFragment.this.checkForProhibitedPhoneNumber(number);
+ }
+
+ @Override
+ public String getQueryString() {
+ String number = SearchFragment.this.getAddToContactNumber();
+ if (number == null) {
+ number = SearchFragment.this.getQueryString();
+ }
+ return number;
+ }
+ });
+ }
+
+
@Override
protected void onItemClick(int position, long id) {
final DialerPhoneNumberListAdapter adapter = (DialerPhoneNumberListAdapter) getAdapter();
@@ -246,7 +284,15 @@
switch (shortcutType) {
case DialerPhoneNumberListAdapter.SHORTCUT_INVALID:
- super.onItemClick(position, id);
+ if (mIsRcsFeatureEnabled) {
+ ContactListItemView v = (ContactListItemView)getListView()
+ .getChildAt(position + 1);
+ if(v != null) {
+ v.getQuickContact().onClick(null);
+ }
+ } else {
+ super.onItemClick(position, id);
+ }
break;
case DialerPhoneNumberListAdapter.SHORTCUT_DIRECT_CALL:
number = adapter.getQueryString();
diff --git a/src/com/android/dialer/list/SmartDialNumberListAdapter.java b/src/com/android/dialer/list/SmartDialNumberListAdapter.java
index ed1f825..de92969 100644
--- a/src/com/android/dialer/list/SmartDialNumberListAdapter.java
+++ b/src/com/android/dialer/list/SmartDialNumberListAdapter.java
@@ -26,6 +26,7 @@
import com.android.contacts.common.CallUtil;
import com.android.contacts.common.list.ContactListItemView;
+import com.android.dialer.EnrichedCallHandler;
import com.android.dialer.dialpad.SmartDialCursorLoader;
import com.android.dialer.dialpad.SmartDialNameMatcher;
import com.android.dialer.dialpad.SmartDialPrefix;
@@ -117,6 +118,8 @@
public void setQueryString(String queryString) {
final boolean showNumberShortcuts = !TextUtils.isEmpty(getFormattedQueryString());
boolean changed = false;
+ changed |= setShortcutEnabled(SHORTCUT_MAKE_RICH_CALL,
+ EnrichedCallHandler.getInstance().isRcsFeatureEnabled() && showNumberShortcuts);
changed |= setShortcutEnabled(SHORTCUT_CREATE_NEW_CONTACT, showNumberShortcuts);
changed |= setShortcutEnabled(SHORTCUT_ADD_TO_EXISTING_CONTACT, showNumberShortcuts);
changed |= setShortcutEnabled(SHORTCUT_SEND_SMS_MESSAGE, showNumberShortcuts);
diff --git a/src/com/android/dialer/list/SmartDialSearchFragment.java b/src/com/android/dialer/list/SmartDialSearchFragment.java
index fcb61ff..93e79a5 100644
--- a/src/com/android/dialer/list/SmartDialSearchFragment.java
+++ b/src/com/android/dialer/list/SmartDialSearchFragment.java
@@ -28,6 +28,7 @@
import com.android.contacts.common.list.ContactEntryListAdapter;
import com.android.contacts.common.util.PermissionsUtil;
+import com.android.dialer.EnrichedCallHandler;
import com.android.dialer.dialpad.SmartDialCursorLoader;
import com.android.dialer.logging.Logger;
import com.android.dialer.logging.ScreenEvent;
@@ -58,6 +59,10 @@
// Set adapter's query string to restore previous instance state.
adapter.setQueryString(getQueryString());
adapter.setListener(this);
+ if (adapter.getEnrichedHelper() != null) {
+ adapter.getEnrichedHelper().setNumberPickerListener(getOnPhoneNumberPickerListener());
+ }
+ EnrichedCallHandler.getInstance().initializeRcsManager();
return adapter;
}