diff options
author | Vinit Deshpande <vinitd@google.com> | 2015-04-16 02:36:02 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-04-16 02:38:21 +0000 |
commit | 2be016ffdd6e28cfa190971c4a0897457d2e216b (patch) | |
tree | 609852deffc666a424ccdac00f8db85694480aca | |
parent | 5b11e0e5d8cbf3fd2b66ce47f5097cf5e6d68aa7 (diff) | |
parent | fddbfbcd6cc080dc706a851374c4cc8bcbf61f81 (diff) |
Merge changes from topic 'mwd-merge-041515'
* changes:
Fix build issues after the merge
am b04d61a..697f674 from mirror-m-wireless-internal-release
27 files changed, 600 insertions, 747 deletions
diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_rx_video_activated_layer.xml b/InCallUI/res/drawable-land/rounded_call_card_background.xml index 750ef5e26..f41ecda79 100644 --- a/InCallUI/res/drawable/ic_lockscreen_answer_rx_video_activated_layer.xml +++ b/InCallUI/res/drawable-land/rounded_call_card_background.xml @@ -1,8 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> + <!-- - ~ Copyright (c) 2014, The Linux Foundation. All rights reserved. - ~ Not a Contribution. - ~ Copyright (C) 2014 The Android Open Source Project + ~ Copyright (C) 2015 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -16,13 +15,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@drawable/fab_red" /> - <item> - <bitmap - android:gravity="center" - android:src="@drawable/ic_rx_videocam" - android:tint="@color/glowpad_widget_active_color" - android:autoMirrored="true" /> - </item> -</layer-list> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@color/incall_call_banner_background_color" /> + <corners + android:radius="4dp" /> +</shape>
\ No newline at end of file diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_rx_video.xml b/InCallUI/res/drawable/ic_lockscreen_answer_rx_video.xml deleted file mode 100644 index c5a41d814..000000000 --- a/InCallUI/res/drawable/ic_lockscreen_answer_rx_video.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (c) 2014, The Linux Foundation. All rights reserved. - ~ Not a Contribution. - ~ Copyright (C) 2014 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<!-- Used with incoming call wigdet. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:state_enabled="true" android:state_active="false" android:state_focused="false" - android:drawable="@drawable/ic_lockscreen_answer_rx_video_normal_layer"/> - <item - android:state_enabled="true" android:state_active="true" android:state_focused="false" - android:drawable="@drawable/ic_lockscreen_answer_rx_video_activated_layer" /> - <item - android:state_enabled="true" android:state_active="false" android:state_focused="true" - android:drawable="@drawable/ic_lockscreen_answer_rx_video_activated_layer" /> -</selector> diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_rx_video_normal_layer.xml b/InCallUI/res/drawable/ic_lockscreen_answer_rx_video_normal_layer.xml deleted file mode 100644 index 5efd3d142..000000000 --- a/InCallUI/res/drawable/ic_lockscreen_answer_rx_video_normal_layer.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (c) 2014, The Linux Foundation. All rights reserved. - ~ Not a Contribution. - ~ Copyright (C) 2014 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- A fake circle to fix the size of this layer asset. --> - <item> - <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> - <solid android:color="#00000000"/> - <size - android:width="@dimen/incoming_call_widget_circle_size" - android:height="@dimen/incoming_call_widget_circle_size" /> - </shape> - </item> - <item> - <bitmap - android:gravity="center" - android:src="@drawable/ic_rx_videocam" - android:tint="@color/glowpad_call_widget_normal_tint" - android:autoMirrored="true" /> - </item> -</layer-list> diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_tx_video.xml b/InCallUI/res/drawable/ic_lockscreen_answer_tx_video.xml deleted file mode 100644 index 15d11978e..000000000 --- a/InCallUI/res/drawable/ic_lockscreen_answer_tx_video.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (c) 2014, The Linux Foundation. All rights reserved. - ~ Not a Contribution. - ~ Copyright (C) 2014 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<!-- Used with incoming call wigdet. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:state_enabled="true" android:state_active="false" android:state_focused="false" - android:drawable="@drawable/ic_lockscreen_answer_tx_video_normal_layer"/> - <item - android:state_enabled="true" android:state_active="true" android:state_focused="false" - android:drawable="@drawable/ic_lockscreen_answer_tx_video_activated_layer" /> - <item - android:state_enabled="true" android:state_active="false" android:state_focused="true" - android:drawable="@drawable/ic_lockscreen_answer_tx_video_activated_layer" /> -</selector> diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_tx_video_normal_layer.xml b/InCallUI/res/drawable/ic_lockscreen_answer_tx_video_normal_layer.xml deleted file mode 100644 index b0ad943dc..000000000 --- a/InCallUI/res/drawable/ic_lockscreen_answer_tx_video_normal_layer.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (c) 2014, The Linux Foundation. All rights reserved. - ~ Not a Contribution. - ~ Copyright (C) 2014 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- A fake circle to fix the size of this layer asset. --> - <item> - <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> - <solid android:color="#00000000"/> - <size - android:width="@dimen/incoming_call_widget_circle_size" - android:height="@dimen/incoming_call_widget_circle_size" /> - </shape> - </item> - <item> - <bitmap - android:gravity="center" - android:src="@drawable/ic_tx_videocam" - android:tint="@color/glowpad_call_widget_normal_tint" - android:autoMirrored="true" /> - </item> -</layer-list> diff --git a/InCallUI/res/layout-land/call_card_fragment.xml b/InCallUI/res/layout-land/call_card_fragment.xml index cdee27970..89466152a 100644 --- a/InCallUI/res/layout-land/call_card_fragment.xml +++ b/InCallUI/res/layout-land/call_card_fragment.xml @@ -30,22 +30,25 @@ android:layout_height="match_parent" android:orientation="vertical" android:elevation="@dimen/primary_call_elevation" - android:background="@color/incall_call_banner_background_color" + android:background="@drawable/rounded_call_card_background" android:paddingTop="@dimen/call_banner_primary_call_container_top_padding" android:clipChildren="false" - android:clipToPadding="false" > + android:clipToPadding="false" + android:alpha="0.9" + android:layout_margin="10dp"> <include layout="@layout/primary_call_info" /> - <fragment android:name="com.android.incallui.CallButtonFragment" - android:id="@+id/callButtonFragment" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - <FrameLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="match_parent" > + <fragment android:name="com.android.incallui.CallButtonFragment" + android:id="@+id/callButtonFragment" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:layout_marginBottom="@dimen/call_buttons_bottom_margin" /> <!-- Secondary "Call info" block, for the background ("on hold") call. --> <include layout="@layout/secondary_call_info" android:layout_width="match_parent" @@ -73,14 +76,6 @@ android:layout_height="wrap_content" android:layout_alignTop="@id/photo" /> - <fragment android:name="com.android.incallui.VideoCallFragment" - android:layout_alignParentStart="true" - android:layout_gravity="start|center_vertical" - android:id="@+id/videoCallFragment" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="gone" /> - <!-- Progress spinner, useful for indicating pending operations such as upgrade to video. --> <FrameLayout android:id="@+id/progressSpinner" @@ -102,7 +97,14 @@ </FrameLayout> - <!-- Placeholder for various fragments that are added dynamically underneath the caller info. --> + <fragment android:name="com.android.incallui.VideoCallFragment" + android:layout_alignParentStart="true" + android:layout_gravity="start|center_vertical" + android:id="@+id/videoCallFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <!-- Placeholder for the dialpad which is replaced with the dialpad fragment when shown. --> <FrameLayout android:id="@+id/answer_and_dialpad_container" android:layout_toEndOf="@id/primary_call_info_container" diff --git a/InCallUI/res/layout/call_button_fragment.xml b/InCallUI/res/layout/call_button_fragment.xml index 604d79e60..3629b8a3b 100644 --- a/InCallUI/res/layout/call_button_fragment.xml +++ b/InCallUI/res/layout/call_button_fragment.xml @@ -67,7 +67,8 @@ <ToggleButton android:id="@+id/audioButton" style="@style/InCallCompoundButton" android:background="@drawable/btn_compound_audio" - android:contentDescription="@string/audio_mode_speaker" /> + android:contentDescription="@string/audio_mode_speaker" + android:visibility="gone" /> <!-- "Change to audio call" for video calls. --> <ImageButton android:id="@+id/changeToVoiceButton" @@ -82,7 +83,8 @@ <ToggleButton android:id="@+id/muteButton" style="@style/InCallCompoundButton" android:background="@drawable/btn_compound_mute" - android:contentDescription="@string/onscreenMuteText" /> + android:contentDescription="@string/onscreenMuteText" + android:visibility="gone" /> <!-- CENTER SLOT ======================================================================= --> @@ -90,7 +92,8 @@ <ToggleButton android:id="@+id/dialpadButton" style="@style/InCallCompoundButton" android:background="@drawable/btn_compound_dialpad" - android:contentDescription="@string/onscreenShowDialpadText" /> + android:contentDescription="@string/onscreenShowDialpadText" + android:visibility="gone" /> <!-- MIDDLE RIGHT SLOT ================================================================= --> @@ -101,7 +104,8 @@ <ToggleButton android:id="@+id/holdButton" style="@style/InCallCompoundButton" android:background="@drawable/btn_compound_hold" - android:contentDescription="@string/onscreenHoldText" /> + android:contentDescription="@string/onscreenHoldText_unselected" + android:visibility="gone" /> <!-- "Swap" (or "Manage calls" in some CDMA states) --> <ImageButton android:id="@+id/swapButton" diff --git a/InCallUI/res/layout/secondary_call_info.xml b/InCallUI/res/layout/secondary_call_info.xml index 85eef0ee1..e866795a6 100644 --- a/InCallUI/res/layout/secondary_call_info.xml +++ b/InCallUI/res/layout/secondary_call_info.xml @@ -40,6 +40,13 @@ android:paddingBottom="@dimen/secondary_call_info_vertical_padding" android:background="?android:attr/selectableItemBackground"> + <ImageView android:id="@+id/secondaryCallVideoCallIcon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_toolbar_video" + android:tint="@color/incall_banner_secondary_text_color" + android:paddingEnd="16dp"/> + <ImageView android:id="@+id/secondaryCallConferenceCallIcon" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/InCallUI/res/menu/incall_overflow_menu.xml b/InCallUI/res/menu/incall_overflow_menu.xml deleted file mode 100644 index 2de858711..000000000 --- a/InCallUI/res/menu/incall_overflow_menu.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2014 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> - -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/overflow_merge_menu_item" - android:title="@string/overflowMergeMenuItemText" /> - - <item android:id="@+id/overflow_add_menu_item" - android:title="@string/overflowAddMenuItemText" /> - - <item android:id="@+id/overflow_hold_menu_item" - android:title="@string/overflowHoldMenuItemText" /> - - <item android:id="@+id/overflow_resume_menu_item" - android:title="@string/overflowResumeMenuItemText" /> - - <item android:id="@+id/overflow_swap_menu_item" - android:title="@string/overflowSwapMenuItemText" /> - - <item android:id="@+id/overflow_manage_conference_menu_item" - android:title="@string/overflowManageConferenceMenuItemText" /> -</menu> diff --git a/InCallUI/res/drawable/ic_lockscreen_answer_tx_video_activated_layer.xml b/InCallUI/res/values-land/colors.xml index c1dca4d06..77eea2e68 100644 --- a/InCallUI/res/drawable/ic_lockscreen_answer_tx_video_activated_layer.xml +++ b/InCallUI/res/values-land/colors.xml @@ -1,8 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (c) 2014, The Linux Foundation. All rights reserved. - ~ Not a Contribution. - ~ Copyright (C) 2014 The Android Open Source Project + ~ Copyright (C) 2015 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -16,13 +14,8 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@drawable/fab_green" /> - <item> - <bitmap - android:gravity="center" - android:src="@drawable/ic_tx_videocam" - android:tint="@color/glowpad_widget_active_color" - android:autoMirrored="true" /> - </item> -</layer-list> + +<resources> + <!-- Background color for status bar. For portrait this will be ignored. --> + <color name="statusbar_background_color">#000000</color> +</resources> diff --git a/InCallUI/res/values-land/dimens.xml b/InCallUI/res/values-land/dimens.xml index d992ccdea..59a5a9a87 100644 --- a/InCallUI/res/values-land/dimens.xml +++ b/InCallUI/res/values-land/dimens.xml @@ -16,6 +16,11 @@ --> <resources> + <!-- Whether or not the landscape mode layout is currently being used --> + <bool name="is_layout_landscape">true</bool> <!-- Height and width of the in-call buttons. --> <dimen name="in_call_button_dimension">40dp</dimen> + <!-- Margin underneath the call buttons. This is used only in landscape mode and is sized + so that the call buttons are center aligned with the end call button. --> + <dimen name="call_buttons_bottom_margin">30dp</dimen> </resources> diff --git a/InCallUI/res/values/array.xml b/InCallUI/res/values/array.xml index 46592e126..2e38c2c46 100644 --- a/InCallUI/res/values/array.xml +++ b/InCallUI/res/values/array.xml @@ -74,8 +74,6 @@ <item>@null</item> <item>@drawable/ic_lockscreen_decline</item> <item>@drawable/ic_lockscreen_answer_video</item> - <item>@drawable/ic_lockscreen_answer_tx_video</item> - <item>@drawable/ic_lockscreen_answer_rx_video</item> </array> <array name="incoming_call_widget_video_without_sms_target_descriptions"> <item>@string/description_target_answer_video_call</item> @@ -100,8 +98,6 @@ <item>@drawable/ic_lockscreen_text</item> <item>@drawable/ic_lockscreen_decline</item> <item>@drawable/ic_lockscreen_answer</item> - <item>@drawable/ic_lockscreen_answer_tx_video</item> - <item>@drawable/ic_lockscreen_answer_rx_video</item> </array> <array name="incoming_call_widget_video_with_sms_target_descriptions"> <item>@string/description_target_answer_video_call</item> @@ -126,8 +122,6 @@ <item>@null</item> <item>@drawable/ic_lockscreen_decline</item> <item>@drawable/ic_lockscreen_answer</item> - <item>@drawable/ic_lockscreen_answer_tx_video</item> - <item>@drawable/ic_lockscreen_answer_rx_video</item> </array> <array name="incoming_call_widget_video_upgrade_request_target_descriptions"> <item>@string/description_target_accept_upgrade_to_video_request</item> @@ -152,26 +146,27 @@ <!-- For accept/reject upgrade to video transmit in active video call - Accept upgrade to video request (drag right) - - Decline upgrade to video request (drag left)--> + - Decline upgrade to video request (drag left) + TODO: This should be automatically rejected in the lower layers --> <array name="incoming_call_widget_video_transmit_accept_reject_request_targets"> - <item>@drawable/ic_lockscreen_answer_tx_video</item> + <item>@drawable/ic_lockscreen_answer_video</item> <item>@drawable/ic_lockscreen_decline</item> </array> <array name="incoming_call_widget_video_transmit_request_target_descriptions"> - <item>@string/description_target_accept_upgrade_to_video_transmit_request</item> - <item>@string/description_target_decline_upgrade_to_video_transmit_request</item> + <item>@string/description_target_accept_upgrade_to_video_request</item> + <item>@string/description_target_decline_upgrade_to_video_request</item> </array> <!-- For accept/reject upgrade to video receive in active video call - Accept upgrade to video request (drag right) - Decline upgrade to video request (drag left)--> <array name="incoming_call_widget_video_receive_accept_reject_request_targets"> - <item>@drawable/ic_lockscreen_answer_rx_video</item> + <item>@drawable/ic_lockscreen_answer_video</item> <item>@drawable/ic_lockscreen_decline</item> </array> <array name="incoming_call_widget_video_receive_request_target_descriptions"> - <item>@string/description_target_accept_upgrade_to_video_receive_request</item> - <item>@string/description_target_decline_upgrade_to_video_receive_request</item> + <item>@string/description_target_accept_upgrade_to_video_request</item> + <item>@string/description_target_decline_upgrade_to_video_request</item> </array> </resources> diff --git a/InCallUI/res/values/colors.xml b/InCallUI/res/values/colors.xml index afc557bc1..cac382deb 100644 --- a/InCallUI/res/values/colors.xml +++ b/InCallUI/res/values/colors.xml @@ -59,6 +59,8 @@ <!-- Background color of action bars --> <color name="actionbar_background_color">@color/dialer_theme_color</color> + <!-- Background color for status bar. For portrait this will be ignored. --> + <color name="statusbar_background_color">@color/dialer_theme_color</color> <color name="translucent_shadow">#33999999</color> diff --git a/InCallUI/res/values/dimens.xml b/InCallUI/res/values/dimens.xml index 43307e8db..0739234f2 100644 --- a/InCallUI/res/values/dimens.xml +++ b/InCallUI/res/values/dimens.xml @@ -16,6 +16,9 @@ --> <resources> + <!-- Whether or not the landscape mode layout is currently being used --> + <bool name="is_layout_landscape">false</bool> + <!-- Dimensions for CallCard elements (the normal in-call UI) --> <!-- Height of the "call banner" overlay on top of the upper part of the call info area. @@ -67,7 +70,7 @@ <dimen name="dialpad_digits_adjustable_height">50dp</dimen> <dimen name="dialpad_key_numbers_size">36dp</dimen> - <dimen name="floating_action_bar_vertical_offset">-24dp</dimen> + <dimen name="floating_action_button_vertical_offset">-24dp</dimen> <dimen name="call_button_margin_vertical">8dp</dimen> <dimen name="call_button_margin_horizontal">6dp</dimen> diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml index 99b8111bb..cfe3d41ab 100644 --- a/InCallUI/res/values/strings.xml +++ b/InCallUI/res/values/strings.xml @@ -255,23 +255,14 @@ to dial using the physical keyboard --> <string name="dialerKeyboardHintText">Use keyboard to dial</string> - <!-- Text for the overflow "Hold call" menu item. --> - <string name="overflowHoldMenuItemText">Hold call</string> - <!-- Text for the overflow "Resume call" menu item. --> - <string name="overflowResumeMenuItemText">Resume call</string> - <!-- Text for the overflow "Add call" menu item. --> - <string name="overflowAddMenuItemText">Add call</string> - <!-- Text for the onscreen "Merge calls" menu item. --> - <string name="overflowMergeMenuItemText">Merge calls</string> - <!-- Text for the onscreen "Swap calls" menu item. --> - <string name="overflowSwapMenuItemText">Swap calls</string> - <!-- Text for the overflow "Manage Conference Video Call" menu item. --> - <string name="overflowManageConferenceMenuItemText">Manage Conference</string> - - <!-- Text for the onscreen "Hold" button --> - <string name="onscreenHoldText">Hold</string> + <!-- Text for the onscreen "Hold" button when it is not selected. Pressing it will put + the call on hold. --> + <string name="onscreenHoldText_unselected">Hold Call</string> + <!-- Text for the onscreen "Hold" button when it is selected. Pressing it will resume + the call from a previously held state. --> + <string name="onscreenHoldText_selected">Resume Call</string> <!-- Text for the onscreen "End call" button --> - <string name="onscreenEndCallText">End</string> + <string name="onscreenEndCallText">End Call</string> <!-- Text for the onscreen "Show Dialpad" button --> <string name="onscreenShowDialpadText">Dialpad</string> <!-- Text for the onscreen "Mute" button --> @@ -456,7 +447,7 @@ <!-- This can be used in any application wanting to disable the text "Emergency number" --> <string name="emergency_call_dialog_number_for_display">Emergency number</string> - <!-- STOPSHIP These strings are for debugging only --> + <!-- STOPSHIP These strings are for debugging only --> <!-- Call substate label --> <string name="call_substate_label" translatable="false">Call substate - \u000a</string> <!-- Call substate label for call resumed --> diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java index a9fafae18..b2c812b7b 100644 --- a/InCallUI/src/com/android/incallui/CallButtonFragment.java +++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java @@ -16,6 +16,8 @@ package com.android.incallui; +import static com.android.incallui.CallButtonFragment.Buttons.*; + import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -30,6 +32,7 @@ import android.os.Bundle; import android.telecom.AudioState; import android.telecom.TelecomManager; import android.telecom.VideoProfile; +import android.util.SparseIntArray; import android.view.ContextThemeWrapper; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; @@ -44,7 +47,6 @@ import android.widget.Toast; import android.widget.PopupMenu.OnDismissListener; import android.widget.PopupMenu.OnMenuItemClickListener; -import com.android.contacts.common.util.MaterialColorMapUtils; import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette; import java.util.ArrayList; @@ -55,8 +57,34 @@ public class CallButtonFragment extends BaseFragment<CallButtonPresenter, CallButtonPresenter.CallButtonUi> implements CallButtonPresenter.CallButtonUi, OnMenuItemClickListener, OnDismissListener, View.OnClickListener { - private CompoundButton mAudioButton; private static final int INVALID_INDEX = -1; + private static final int BUTTON_MAX_VISIBLE = 5; + // The button is currently visible in the UI + private static final int BUTTON_VISIBLE = 1; + // The button is hidden in the UI + private static final int BUTTON_HIDDEN = 2; + // The button has been collapsed into the overflow menu + private static final int BUTTON_MENU = 3; + + public interface Buttons { + public static final int BUTTON_AUDIO = 0; + public static final int BUTTON_DOWNGRADE_TO_VOICE = 1; + public static final int BUTTON_MUTE = 2; + public static final int BUTTON_DIALPAD = 3; + public static final int BUTTON_HOLD = 4; + public static final int BUTTON_SWAP = 5; + public static final int BUTTON_UPGRADE_TO_VIDEO = 6; + public static final int BUTTON_SWITCH_CAMERA = 7; + public static final int BUTTON_ADD_CALL = 8; + public static final int BUTTON_MERGE = 9; + public static final int BUTTON_PAUSE_VIDEO = 10; + public static final int BUTTON_MANAGE_VIDEO_CONFERENCE = 11; + public static final int BUTTON_COUNT = 12; + } + + private SparseIntArray mButtonVisibilityMap = new SparseIntArray(BUTTON_COUNT); + + private CompoundButton mAudioButton; private ImageButton mChangeToVoiceButton; private CompoundButton mMuteButton; private CompoundButton mShowDialpadButton; @@ -97,6 +125,10 @@ public class CallButtonFragment @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + for (int i = 0; i < BUTTON_COUNT; i++) { + mButtonVisibilityMap.put(i, BUTTON_HIDDEN); + } } @Override @@ -157,7 +189,6 @@ public class CallButtonFragment int id = view.getId(); Log.d(this, "onClick(View " + view + ", id " + id + ")..."); - boolean isClickHandled = true; switch(id) { case R.id.audioButton: onAudioButtonClicked(); @@ -200,22 +231,21 @@ public class CallButtonFragment !mPauseVideoButton.isSelected() /* pause */); break; case R.id.overflowButton: - mOverflowPopup.show(); + if (mOverflowPopup != null) { + mOverflowPopup.show(); + } break; case R.id.manageVideoCallConferenceButton: onManageVideoCallConferenceClicked(); break; default: - isClickHandled = false; Log.wtf(this, "onClick: unexpected"); - break; + return; } - if (isClickHandled) { - view.performHapticFeedback( - HapticFeedbackConstants.VIRTUAL_KEY, - HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - } + view.performHapticFeedback( + HapticFeedbackConstants.VIRTUAL_KEY, + HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); } public void updateColors() { @@ -225,7 +255,6 @@ public class CallButtonFragment return; } - Resources res = getActivity().getResources(); View[] compoundButtons = { mAudioButton, mMuteButton, @@ -344,112 +373,81 @@ public class CallButtonFragment } @Override - public void setMute(boolean value) { - if (mMuteButton.isSelected() != value) { - mMuteButton.setSelected(value); - } - } - - @Override - public void showAudioButton(boolean show) { - mAudioButton.setVisibility(show ? View.VISIBLE : View.GONE); - } - - @Override - public void showChangeToVoiceButton(boolean show) { - mChangeToVoiceButton.setVisibility(show ? View.VISIBLE : View.GONE); + public void showButton(int buttonId, boolean show) { + mButtonVisibilityMap.put(buttonId, show ? BUTTON_VISIBLE : BUTTON_HIDDEN); } @Override - public void enableMute(boolean enabled) { - mMuteButton.setEnabled(enabled); + public void enableButton(int buttonId, boolean enable) { + final View button = getButtonById(buttonId); + if (button != null) { + button.setEnabled(enable); + } } - @Override - public void showDialpadButton(boolean show) { - mShowDialpadButton.setVisibility(show ? View.VISIBLE : View.GONE); + private View getButtonById(int id) { + switch (id) { + case BUTTON_AUDIO: + return mAudioButton; + case BUTTON_DOWNGRADE_TO_VOICE: + return mChangeToVoiceButton; + case BUTTON_MUTE: + return mMuteButton; + case BUTTON_DIALPAD: + return mShowDialpadButton; + case BUTTON_HOLD: + return mHoldButton; + case BUTTON_SWAP: + return mSwapButton; + case BUTTON_UPGRADE_TO_VIDEO: + return mChangeToVideoButton; + case BUTTON_SWITCH_CAMERA: + return mSwitchCameraButton; + case BUTTON_ADD_CALL: + return mAddCallButton; + case BUTTON_MERGE: + return mMergeButton; + case BUTTON_PAUSE_VIDEO: + return mPauseVideoButton; + case BUTTON_MANAGE_VIDEO_CONFERENCE: + return mManageVideoCallConferenceButton; + default: + Log.w(this, "Invalid button id"); + return null; + } } @Override public void setHold(boolean value) { if (mHoldButton.isSelected() != value) { mHoldButton.setSelected(value); + mHoldButton.setContentDescription(getContext().getString( + value ? R.string.onscreenHoldText_selected + : R.string.onscreenHoldText_unselected)); } } @Override - public void showHoldButton(boolean show) { - mHoldButton.setVisibility(show ? View.VISIBLE : View.GONE); - } - - @Override - public void enableHold(boolean enabled) { - mHoldButton.setEnabled(enabled); - } - - @Override - public void showSwapButton(boolean show) { - mSwapButton.setVisibility(show ? View.VISIBLE : View.GONE); - } - - @Override - public void showChangeToVideoButton(boolean show) { - mChangeToVideoButton.setVisibility(show ? View.VISIBLE : View.GONE); - } - - @Override - public void enableChangeToVideoButton(boolean enable) { - mChangeToVideoButton.setEnabled(enable); - } - - @Override - public void showSwitchCameraButton(boolean show) { - mSwitchCameraButton.setVisibility(show ? View.VISIBLE : View.GONE); - } - - @Override - public void setSwitchCameraButton(boolean isBackFacingCamera) { + public void setCameraSwitched(boolean isBackFacingCamera) { mSwitchCameraButton.setSelected(isBackFacingCamera); } @Override - public void showAddCallButton(boolean show) { - Log.d(this, "show Add call button: " + show); - mAddCallButton.setVisibility(show ? View.VISIBLE : View.GONE); - } - - public void showManageConferenceVideoCallButton(boolean show) { - mManageVideoCallConferenceButton.setVisibility(show ? View.VISIBLE : View.GONE); - } - - @Override - public void showMergeButton(boolean show) { - mMergeButton.setVisibility(show ? View.VISIBLE : View.GONE); - - // If the merge button was disabled, re-enable it when hiding it. - if (!show) { - mMergeButton.setEnabled(true); - } - } - - @Override - public void showPauseVideoButton(boolean show) { - mPauseVideoButton.setVisibility(show ? View.VISIBLE : View.GONE); - } - - @Override - public void setPauseVideoButton(boolean isPaused) { + public void setVideoPaused(boolean isPaused) { mPauseVideoButton.setSelected(isPaused); } @Override - public void showOverflowButton(boolean show) { - mOverflowButton.setVisibility(show ? View.VISIBLE : View.GONE); + public void setMute(boolean value) { + if (mMuteButton.isSelected() != value) { + mMuteButton.setSelected(value); + } } /**The function is called when Modify Call button gets pressed. The function creates and * displays modify call options. */ + @Override public void displayModifyCallOptions() { CallButtonPresenter.CallButtonUi ui = getUi(); if (ui == null) { @@ -520,65 +518,68 @@ public class CallButtonFragment return ""; } + private void addToOverflowMenu(int id, View button, PopupMenu menu) { + button.setVisibility(View.GONE); + menu.getMenu().add(Menu.NONE, id, Menu.NONE, button.getContentDescription()); + mButtonVisibilityMap.put(id, BUTTON_MENU); + } + + private PopupMenu getPopupMenu() { + return new PopupMenu(new ContextThemeWrapper(getActivity(), R.style.InCallPopupMenuStyle), + mOverflowButton); + } + + /** + * Iterates through the list of buttons and toggles their visibility depending on the + * setting configured by the CallButtonPresenter. If there are more visible buttons than + * the allowed maximum, the excess buttons are collapsed into a single overflow menu. + */ @Override - public void configureOverflowMenu(boolean showMergeMenuOption, boolean showAddMenuOption, - boolean showHoldMenuOption, boolean showSwapMenuOption, - boolean showManageConferenceVideoCallOption) { - if (mOverflowPopup == null) { - final ContextThemeWrapper contextWrapper = new ContextThemeWrapper(getActivity(), - R.style.InCallPopupMenuStyle); - mOverflowPopup = new PopupMenu(contextWrapper, mOverflowButton); - mOverflowPopup.getMenuInflater().inflate(R.menu.incall_overflow_menu, - mOverflowPopup.getMenu()); + public void updateButtonStates() { + View prevVisibleButton = null; + int prevVisibleId = -1; + PopupMenu menu = null; + int visibleCount = 0; + for (int i = 0; i < BUTTON_COUNT; i++) { + final int visibility = mButtonVisibilityMap.get(i); + final View button = getButtonById(i); + if (visibility == BUTTON_VISIBLE) { + visibleCount++; + if (visibleCount <= BUTTON_MAX_VISIBLE) { + button.setVisibility(View.VISIBLE); + prevVisibleButton = button; + prevVisibleId = i; + } else { + if (menu == null) { + menu = getPopupMenu(); + } + // Collapse the current button into the overflow menu. If is the first visible + // button that exceeds the threshold, also collapse the previous visible button + // so that the total number of visible buttons will never exceed the threshold. + if (prevVisibleButton != null) { + addToOverflowMenu(prevVisibleId, prevVisibleButton, menu); + prevVisibleButton = null; + prevVisibleId = -1; + } + addToOverflowMenu(i, button, menu); + } + } else if (visibility == BUTTON_HIDDEN){ + button.setVisibility(View.GONE); + } + } + + mOverflowButton.setVisibility(menu != null ? View.VISIBLE : View.GONE); + if (menu != null) { + mOverflowPopup = menu; mOverflowPopup.setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.overflow_merge_menu_item: - getPresenter().mergeClicked(); - break; - case R.id.overflow_add_menu_item: - getPresenter().addCallClicked(); - break; - case R.id.overflow_hold_menu_item: - getPresenter().holdClicked(true /* checked */); - break; - case R.id.overflow_resume_menu_item: - getPresenter().holdClicked(false /* checked */); - break; - case R.id.overflow_swap_menu_item: - getPresenter().addCallClicked(); - break; - case R.id.overflow_manage_conference_menu_item: - onManageVideoCallConferenceClicked(); - break; - default: - Log.wtf(this, "onMenuItemClick: unexpected overflow menu click"); - break; - } + final int id = item.getItemId(); + getButtonById(id).performClick(); return true; } }); - mOverflowPopup.setOnDismissListener(new OnDismissListener() { - @Override - public void onDismiss(PopupMenu popupMenu) { - popupMenu.dismiss(); - } - }); } - - final Menu menu = mOverflowPopup.getMenu(); - menu.findItem(R.id.overflow_merge_menu_item).setVisible(showMergeMenuOption); - menu.findItem(R.id.overflow_add_menu_item).setVisible(showAddMenuOption); - menu.findItem(R.id.overflow_hold_menu_item).setVisible( - showHoldMenuOption && !mHoldButton.isSelected()); - menu.findItem(R.id.overflow_resume_menu_item).setVisible( - showHoldMenuOption && mHoldButton.isSelected()); - menu.findItem(R.id.overflow_swap_menu_item).setVisible(showSwapMenuOption); - menu.findItem(R.id.overflow_manage_conference_menu_item).setVisible( - showManageConferenceVideoCallOption); - - mOverflowButton.setEnabled(menu.hasVisibleItems()); } @Override diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java index b50827fea..43431c73c 100644 --- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java +++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java @@ -16,7 +16,8 @@ package com.android.incallui; -import android.app.AlertDialog; +import static com.android.incallui.CallButtonFragment.Buttons.*; + import android.content.Context; import android.os.Bundle; import android.telecom.AudioState; @@ -46,7 +47,6 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto private Call mCall; private boolean mAutomaticallyMuted = false; private boolean mPreviousMuteState = false; - private static final int BUTTON_THRESOLD_TO_DISPLAY_OVERFLOW_MENU = 5; public CallButtonPresenter() { } @@ -119,7 +119,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto @Override public void onDetailsChanged(Call call, android.telecom.Call.Details details) { if (getUi() != null && Objects.equals(call, mCall)) { - updateCallButtons(call, getUi().getContext()); + updateButtonsState(call); } } @@ -131,7 +131,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto @Override public void onCanAddCallChanged(boolean canAddCall) { if (getUi() != null && mCall != null) { - updateCallButtons(mCall, getUi().getContext()); + updateButtonsState(mCall); } } @@ -321,7 +321,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto mCall.getVideoState() & ~VideoProfile.VideoState.PAUSED); videoCall.sendSessionModifyRequest(videoProfile); } - getUi().setPauseVideoButton(pause); + getUi().setVideoPaused(pause); } private void updateUi(InCallState state, Call call) { @@ -340,9 +340,9 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto return; } - updateCallButtons(call, ui.getContext()); + updateButtonsState(call); - ui.enableMute(call.can(android.telecom.Call.Details.CAPABILITY_MUTE)); + ui.enableButton(BUTTON_MUTE, call.can(android.telecom.Call.Details.CAPABILITY_MUTE)); } private static int toInteger(boolean b) { @@ -353,137 +353,49 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto * Updates the buttons applicable for the UI. * * @param call The active call. - * @param context The context. */ - private void updateCallButtons(Call call, Context context) { - if (CallUtils.isVideoCall(call)) { - updateVideoCallButtons(call); - } - updateVoiceCallButtons(call); - } - - private void updateVideoCallButtons(Call call) { - Log.v(this, "Showing buttons for video call."); - final CallButtonUi ui = getUi(); - - // Show all video-call-related buttons. - ui.showSwitchCameraButton(true); - ui.showPauseVideoButton(true); - - final boolean supportHold = call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD); - final boolean enableHoldOption = call.can(android.telecom.Call.Details.CAPABILITY_HOLD); - ui.showHoldButton(supportHold); - ui.enableHold(enableHoldOption); - ui.setHold(call.getState() == Call.State.ONHOLD); - } - - private void updateVoiceCallButtons(Call call) { + private void updateButtonsState(Call call) { Log.v(this, "Showing buttons for voice call."); final CallButtonUi ui = getUi(); - // Hide all video-call-related buttons. - ui.showChangeToVoiceButton(false); - ui.showSwitchCameraButton(false); - ui.showPauseVideoButton(false); - - // Show all voice-call-related buttons. - ui.showAudioButton(true); - ui.showDialpadButton(true); - - Log.v(this, "Show hold ", call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)); - Log.v(this, "Enable hold", call.can(android.telecom.Call.Details.CAPABILITY_HOLD)); - Log.v(this, "Show merge ", call.can( - android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE)); - Log.v(this, "Show swap ", call.can( - android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE)); - Log.v(this, "Show add call ", TelecomAdapter.getInstance().canAddCall()); - Log.v(this, "Show mute ", call.can(android.telecom.Call.Details.CAPABILITY_MUTE)); - - boolean canBidiLocal = - call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL); - boolean canBidiRemote = - call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL); - Log.v(this, "Show video call local:" + canBidiLocal + ", remote: " + canBidiRemote); - - final boolean canAdd = TelecomAdapter.getInstance().canAddCall(); - final boolean enableHoldOption = call.can(android.telecom.Call.Details.CAPABILITY_HOLD); - final boolean supportHold = call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD); - final boolean isCallOnHold = call.getState() == Call.State.ONHOLD; - - boolean canVideoCall = canBidiLocal && canBidiRemote; - ui.showChangeToVideoButton(canVideoCall); - - final boolean showMergeOption = call.can( - android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE); - final boolean showAddCallOption = canAdd; - final boolean showManageVideoCallConferenceOption = call.can( - android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE) - && CallUtils.isVideoCall(call); + final boolean isVideo = CallUtils.isVideoCall(call); + // Common functionality (audio, hold, etc). // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available: // (1) If the device normally can hold, show HOLD in a disabled state. // (2) If the device doesn't have the concept of hold/swap, remove the button. - final boolean showSwapOption = call.can( + final boolean showSwap = call.can( android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE); - final boolean showHoldOption = !showSwapOption && (enableHoldOption || supportHold); + final boolean showHold = !showSwap + && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD) + && call.can(android.telecom.Call.Details.CAPABILITY_HOLD); + final boolean isCallOnHold = call.getState() == Call.State.ONHOLD; + + final boolean showAddCall = TelecomAdapter.getInstance().canAddCall(); + final boolean showMerge = call.can( + android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE); + final boolean showUpgradeToVideo = !isVideo + && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX) + && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX); + final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE); + + ui.showButton(BUTTON_AUDIO, true); + ui.showButton(BUTTON_SWAP, showSwap); + ui.showButton(BUTTON_HOLD, showHold); ui.setHold(isCallOnHold); - //Initialize buttonCount = 2. Because speaker and dialpad these two always show in Call UI. - int buttonCount = 2; - buttonCount += toInteger(canVideoCall); - buttonCount += toInteger(showAddCallOption); - buttonCount += toInteger(showMergeOption); - buttonCount += toInteger(showHoldOption); - buttonCount += toInteger(showSwapOption); - buttonCount += toInteger(call.can(android.telecom.Call.Details.CAPABILITY_MUTE)); - buttonCount += toInteger(showManageVideoCallConferenceOption); - - Log.v(this, "show ManageVideoCallConference: " + showManageVideoCallConferenceOption); - Log.v(this, "No of InCall buttons: " + buttonCount + " canVideoCall: " + canVideoCall); - - // Show overflow menu if number of buttons is greater than 5. - final boolean showOverflowMenu = - buttonCount > BUTTON_THRESOLD_TO_DISPLAY_OVERFLOW_MENU; - final boolean isVideoOverflowScenario = canVideoCall && showOverflowMenu; - final boolean isOverflowScenario = !canVideoCall && showOverflowMenu; - - if (isVideoOverflowScenario) { - ui.showHoldButton(false); - ui.showSwapButton(false); - ui.showAddCallButton(false); - ui.showMergeButton(false); - ui.showManageConferenceVideoCallButton(false); - - ui.configureOverflowMenu( - showMergeOption, - showAddCallOption /* showAddMenuOption */, - showHoldOption && enableHoldOption /* showHoldMenuOption */, - showSwapOption, - showManageVideoCallConferenceOption); - ui.showOverflowButton(true); - } else { - if (isOverflowScenario) { - ui.showAddCallButton(false); - ui.showMergeButton(false); - ui.showManageConferenceVideoCallButton(false); - - ui.configureOverflowMenu( - showMergeOption, - showAddCallOption /* showAddMenuOption */, - false /* showHoldMenuOption */, - false /* showSwapMenuOption */, - showManageVideoCallConferenceOption); - } else { - ui.showMergeButton(showMergeOption); - ui.showAddCallButton(showAddCallOption); - ui.showManageConferenceVideoCallButton(showManageVideoCallConferenceOption); - } + ui.showButton(BUTTON_MUTE, showMute); + ui.showButton(BUTTON_ADD_CALL, showAddCall); + // TODO: This button is currently being used for both upgrade and downgrade scenarios. + // It should be split into BUTTON_DOWNGRADE_TO_VOICE AND BUTTON_UPGRADE_TO_VIDEO + ui.showButton(BUTTON_UPGRADE_TO_VIDEO, true); + ui.showButton(BUTTON_DOWNGRADE_TO_VOICE, false); + ui.showButton(BUTTON_SWITCH_CAMERA, isVideo); + ui.showButton(BUTTON_PAUSE_VIDEO, isVideo); + ui.showButton(BUTTON_DIALPAD, !isVideo); + ui.showButton(BUTTON_MERGE, showMerge); - ui.showOverflowButton(isOverflowScenario); - ui.showHoldButton(showHoldOption); - ui.enableHold(enableHoldOption); - ui.showSwapButton(showSwapOption); - } + ui.updateButtonStates(); } public void refreshMuteState() { @@ -515,34 +427,24 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto } public interface CallButtonUi extends Ui { + void showButton(int buttonId, boolean show); + void enableButton(int buttonId, boolean enable); void setEnabled(boolean on); void setMute(boolean on); - void enableMute(boolean enabled); - void showAudioButton(boolean show); - void showChangeToVoiceButton(boolean show); - void showDialpadButton(boolean show); void setHold(boolean on); - void showHoldButton(boolean show); - void enableHold(boolean enabled); - void showSwapButton(boolean show); - void showChangeToVideoButton(boolean show); - void enableChangeToVideoButton(boolean enable); - void showSwitchCameraButton(boolean show); - void setSwitchCameraButton(boolean isBackFacingCamera); - void showAddCallButton(boolean show); - void showManageConferenceVideoCallButton(boolean show); - void showMergeButton(boolean show); - void showPauseVideoButton(boolean show); - void setPauseVideoButton(boolean isPaused); - void showOverflowButton(boolean show); + void setCameraSwitched(boolean isBackFacingCamera); + void setVideoPaused(boolean isPaused); + void setAudio(int mode); + void setSupportedAudio(int mask); void displayDialpad(boolean on, boolean animate); void displayModifyCallOptions(); boolean isDialpadVisible(); - void setAudio(int mode); - void setSupportedAudio(int mask); - void configureOverflowMenu(boolean showMergeMenuOption, boolean showAddMenuOption, - boolean showHoldMenuOption, boolean showSwapMenuOption, - boolean showManageConferenceVideoCallOption); + + /** + * Once showButton() has been called on each of the individual buttons in the UI, call + * this to configure the overflow menu appropriately. + */ + void updateButtonStates(); Context getContext(); } @@ -551,6 +453,6 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto if (getUi() == null) { return; } - getUi().setSwitchCameraButton(!isUsingFrontFacingCamera); + getUi().setCameraSwitched(!isUsingFrontFacingCamera); } } diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java index 7420ba90e..abe4607c1 100644 --- a/InCallUI/src/com/android/incallui/CallCardFragment.java +++ b/InCallUI/src/com/android/incallui/CallCardFragment.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.os.Trace; import android.telecom.DisconnectCause; @@ -96,6 +97,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr private View mSecondaryCallProviderInfo; private TextView mSecondaryCallProviderLabel; private View mSecondaryCallConferenceCallIcon; + private View mSecondaryCallVideoCallIcon; private View mProgressSpinner; private View mManageConferenceCallButton; @@ -134,7 +136,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr mShrinkAnimationDuration = getResources().getInteger(R.integer.shrink_animation_duration); mVideoAnimationDuration = getResources().getInteger(R.integer.video_animation_duration); mFloatingActionButtonVerticalOffset = getResources().getDimensionPixelOffset( - R.dimen.floating_action_bar_vertical_offset); + R.dimen.floating_action_button_vertical_offset); mFabNormalDiameter = getResources().getDimensionPixelOffset( R.dimen.end_call_floating_action_button_diameter); mFabSmallDiameter = getResources().getDimensionPixelOffset( @@ -156,9 +158,8 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr Trace.beginSection(TAG + " onCreate"); mTranslationOffset = getResources().getDimensionPixelSize(R.dimen.call_card_anim_translate_y_offset); - final View view = inflater.inflate(R.layout.call_card_fragment, container, false); - Trace.endSection(); - return view; + + return inflater.inflate(R.layout.call_card_fragment, container, false); } @Override @@ -285,17 +286,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr float videoViewTranslation = 0f; // Translate the call card to its pre-animation state. - if (mIsLandscape) { - float translationX = mPrimaryCallCardContainer.getWidth(); - translationX *= isLayoutRtl ? 1 : -1; - - mPrimaryCallCardContainer.setTranslationX(visible ? translationX : 0); - - if (visible) { - videoViewTranslation = videoView.getWidth() / 2 - spaceBesideCallCard / 2; - videoViewTranslation *= isLayoutRtl ? -1 : 1; - } - } else { + if (!mIsLandscape){ mPrimaryCallCardContainer.setTranslationY(visible ? -mPrimaryCallCardContainer.getHeight() : 0); @@ -458,7 +449,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr @Override public void setSecondary(boolean show, String name, boolean nameIsNumber, String label, - String providerLabel, boolean isConference) { + String providerLabel, boolean isConference, boolean isVideoCall) { if (show != mSecondaryCallInfo.isShown()) { updateFabPositionForSecondaryCallInfo(); @@ -469,6 +460,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr showAndInitializeSecondaryCallInfo(hasProvider); mSecondaryCallConferenceCallIcon.setVisibility(isConference ? View.VISIBLE : View.GONE); + mSecondaryCallVideoCallIcon.setVisibility(isVideoCall ? View.VISIBLE : View.GONE); mSecondaryCallName.setText(nameIsNumber ? PhoneNumberUtils.getPhoneTtsSpannable(name) @@ -760,6 +752,8 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr mSecondaryCallName = (TextView) getView().findViewById(R.id.secondaryCallName); mSecondaryCallConferenceCallIcon = getView().findViewById(R.id.secondaryCallConferenceCallIcon); + mSecondaryCallVideoCallIcon = + getView().findViewById(R.id.secondaryCallVideoCallIcon); } if (mSecondaryCallProviderLabel == null && hasProvider) { @@ -849,7 +843,13 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr return; } - mPrimaryCallCardContainer.setBackgroundColor(themeColors.mPrimaryColor); + if (getResources().getBoolean(R.bool.is_layout_landscape)) { + final GradientDrawable drawable = + (GradientDrawable) mPrimaryCallCardContainer.getBackground(); + drawable.setColor(themeColors.mPrimaryColor); + } else { + mPrimaryCallCardContainer.setBackgroundColor(themeColors.mPrimaryColor); + } mCallButtonsContainer.setBackgroundColor(themeColors.mPrimaryColor); mCurrentThemeColors = themeColors; @@ -963,8 +963,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr mAnimatorSet.cancel(); } - mIsLandscape = getResources().getConfiguration().orientation - == Configuration.ORIENTATION_LANDSCAPE; + mIsLandscape = getResources().getBoolean(R.bool.is_layout_landscape); final ViewGroup parent = ((ViewGroup) mPrimaryCallCardContainer.getParent()); final ViewTreeObserver observer = parent.getViewTreeObserver(); diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java index bddeea351..535ba3bf0 100644 --- a/InCallUI/src/com/android/incallui/CallCardPresenter.java +++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java @@ -254,13 +254,11 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> } maybeShowManageConferenceCallButton(); + maybeShowProgressSpinner(); - final boolean enableEndCallButton = (Call.State.isConnectingOrConnected(callState) - || callState == Call.State.DISCONNECTING) && - callState != Call.State.INCOMING && mPrimary != null; // Hide the end call button instantly if we're receiving an incoming call. - getUi().setEndCallButtonEnabled( - enableEndCallButton, callState != Call.State.INCOMING /* animate */); + getUi().setEndCallButtonEnabled(shouldShowEndCallButton(mPrimary, callState), + callState != Call.State.INCOMING /* animate */); } @Override @@ -315,6 +313,13 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> getUi().showManageConferenceCallButton(shouldShowManageConference()); } + private void maybeShowProgressSpinner() { + final boolean show = mPrimary != null && mPrimary.getSessionModificationState() + == Call.SessionModificationState.WAITING_FOR_RESPONSE + && mPrimary.getState() == Call.State.ACTIVE; + getUi().setProgressSpinnerVisible(show); + } + /** * Determines if the manage conference button should be visible, based on the current primary * call. @@ -531,7 +536,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> if (mSecondary == null) { // Clear the secondary display info. - ui.setSecondary(false, null, false, null, null, false /* isConference */); + ui.setSecondary(false, null, false, null, null, false /* isConference */, + false /* isVideoCall */); return; } @@ -542,7 +548,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> false /* nameIsNumber */, null /* label */, getCallProviderLabel(mSecondary), - true /* isConference */); + true /* isConference */, + mSecondary.isVideoCall(mContext)); } else if (mSecondaryContactInfo != null) { Log.d(TAG, "updateSecondaryDisplayInfo() " + mSecondaryContactInfo); String name = getNameForCall(mSecondaryContactInfo); @@ -553,10 +560,12 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> nameIsNumber, mSecondaryContactInfo.label, getCallProviderLabel(mSecondary), - false /* isConference */); + false /* isConference */, + mSecondary.isVideoCall(mContext)); } else { // Clear the secondary display info. - ui.setSecondary(false, null, false, null, null, false /* isConference */); + ui.setSecondary(false, null, false, null, null, false /* isConference */, + false /* isVideoCall */); } } @@ -736,13 +745,27 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> return photo; } + private boolean shouldShowEndCallButton(Call primary, int callState) { + if (primary == null) { + return false; + } + if (!Call.State.isConnectingOrConnected(callState) || callState == Call.State.INCOMING) { + return false; + } + if (mPrimary.getSessionModificationState() + == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { + return false; + } + return true; + } + public interface CallCardUi extends Ui { void setVisible(boolean on); void setCallCardVisible(boolean visible); void setPrimary(String number, String name, boolean nameIsNumber, String label, Drawable photo, boolean isSipCall); void setSecondary(boolean show, String name, boolean nameIsNumber, String label, - String providerLabel, boolean isConference); + String providerLabel, boolean isConference, boolean isVideoCall); void setCallState(int state, int videoState, int sessionModificationState, DisconnectCause disconnectCause, String connectionLabel, Drawable connectionIcon, String gatewayNumber, boolean isWifi); diff --git a/InCallUI/src/com/android/incallui/DialpadFragment.java b/InCallUI/src/com/android/incallui/DialpadFragment.java index 90610a82d..f185c54bc 100644 --- a/InCallUI/src/com/android/incallui/DialpadFragment.java +++ b/InCallUI/src/com/android/incallui/DialpadFragment.java @@ -422,7 +422,7 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View parent = inflater.inflate( - com.android.incallui.R.layout.incall_dialpad_fragment, container, false); + R.layout.incall_dialpad_fragment, container, false); mDialpadView = (DialpadView) parent.findViewById(R.id.dialpad_view); mDialpadView.setCanDigitsBeEdited(false); mDialpadView.setBackgroundResource(R.color.incall_dialpad_background); diff --git a/InCallUI/src/com/android/incallui/GlowPadWrapper.java b/InCallUI/src/com/android/incallui/GlowPadWrapper.java index 584ce65de..58a5f30ea 100644 --- a/InCallUI/src/com/android/incallui/GlowPadWrapper.java +++ b/InCallUI/src/com/android/incallui/GlowPadWrapper.java @@ -128,14 +128,6 @@ public class GlowPadWrapper extends GlowPadView implements GlowPadView.OnTrigger mAnswerListener.onAnswer(VideoProfile.VideoState.BIDIRECTIONAL, getContext()); mTargetTriggered = true; break; - case R.drawable.ic_lockscreen_answer_tx_video: - mAnswerListener.onAnswer(VideoProfile.VideoState.TX_ENABLED, getContext()); - mTargetTriggered = true; - break; - case R.drawable.ic_lockscreen_answer_rx_video: - mAnswerListener.onAnswer(VideoProfile.VideoState.RX_ENABLED, getContext()); - mTargetTriggered = true; - break; case R.drawable.ic_toolbar_video_off: InCallPresenter.getInstance().declineUpgradeRequest(getContext()); mTargetTriggered = true; diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java index 1283177d6..5ecb96e3f 100644 --- a/InCallUI/src/com/android/incallui/InCallActivity.java +++ b/InCallUI/src/com/android/incallui/InCallActivity.java @@ -473,16 +473,11 @@ public class InCallActivity extends Activity implements FragmentDisplayManager { return false; } - @Override - public void onConfigurationChanged(Configuration config) { - InCallPresenter.getInstance().getProximitySensor().onConfigurationChanged(config); - Log.d(this, "onConfigurationChanged "+config.orientation); - - doOrientationChanged(config.orientation); - super.onConfigurationChanged(config); - } - - + /** + * Handles changes in device orientation. + * + * @param orientation The new device orientation. + */ private void doOrientationChanged(int orientation) { Log.d(this, "doOrientationChanged prevOrientation=" + sCurrentOrientation + " newOrientation=" + orientation); diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java index fdf193318..d5aa0e500 100644 --- a/InCallUI/src/com/android/incallui/InCallPresenter.java +++ b/InCallUI/src/com/android/incallui/InCallPresenter.java @@ -20,6 +20,7 @@ import android.app.FragmentManager; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.res.Resources; import android.graphics.Point; import android.net.Uri; import android.os.Bundle; @@ -179,7 +180,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener, /** * When configuration changes Android kills the current activity and starts a new one. - * The flag is used to check if full clean up is necessary (activity is stopped and new + * The flag is used to check if full clean up is necessary (activity is stopped and new * activity won't be started), or if a new activity will be started right after the current one * is destroyed, and therefore no need in release all resources. */ @@ -1446,7 +1447,13 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener, return; } - mInCallActivity.getWindow().setStatusBarColor(mThemeColors.mSecondaryColor); + final Resources resources = mInCallActivity.getResources(); + if (resources.getBoolean(R.bool.is_layout_landscape)) { + mInCallActivity.getWindow().setStatusBarColor( + resources.getColor(R.color.statusbar_background_color)); + } else { + mInCallActivity.getWindow().setStatusBarColor(mThemeColors.mSecondaryColor); + } } /** diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallListener.java b/InCallUI/src/com/android/incallui/InCallVideoCallListener.java index df9dfdce7..741b8d614 100644 --- a/InCallUI/src/com/android/incallui/InCallVideoCallListener.java +++ b/InCallUI/src/com/android/incallui/InCallVideoCallListener.java @@ -87,8 +87,10 @@ public class InCallVideoCallListener extends VideoCall.Listener { boolean isVideoCall = VideoProfile.VideoState.isVideo(responseProfile.getVideoState()); if (modifySucceeded && isVideoCall) { InCallVideoCallListenerNotifier.getInstance().upgradeToVideoSuccess(mCall); - } else if (!modifySucceeded) { + } else if (!modifySucceeded && isVideoCall) { InCallVideoCallListenerNotifier.getInstance().upgradeToVideoFail(status, mCall); + } else if (modifySucceeded && !isVideoCall) { + InCallVideoCallListenerNotifier.getInstance().downgradeToAudio(mCall); } } else { Log.d(this, "onSessionModifyResponseReceived request and response Profiles are null"); diff --git a/InCallUI/src/com/android/incallui/VideoCallFragment.java b/InCallUI/src/com/android/incallui/VideoCallFragment.java index d0ef13a50..fb29c9ce6 100644 --- a/InCallUI/src/com/android/incallui/VideoCallFragment.java +++ b/InCallUI/src/com/android/incallui/VideoCallFragment.java @@ -19,6 +19,7 @@ package com.android.incallui; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Matrix; import android.graphics.Point; import android.graphics.SurfaceTexture; import android.os.Bundle; @@ -90,11 +91,6 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, private View mVideoViews; /** - * {@code True} when the entering the activity again after a restart due to orientation change. - */ - private boolean mIsActivityRestart; - - /** * {@code True} when the layout of the activity has been completed. */ private boolean mIsLayoutComplete = false; @@ -404,12 +400,20 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, Log.e(this, "onClick: Presenter is null."); } } + + /** + * Returns the dimensions of the surface. + * + * @return The dimensions of the surface. + */ + public Point getSurfaceDimensions() { + return new Point(mWidth, mHeight); + } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mIsActivityRestart = sVideoSurfacesInUse; } /** @@ -421,21 +425,12 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mIsLandscape = getResources().getConfiguration().orientation - == Configuration.ORIENTATION_LANDSCAPE; + mIsLandscape = getResources().getBoolean(R.bool.is_layout_landscape); Log.d(this, "onActivityCreated: IsLandscape=" + mIsLandscape); getPresenter().init(getActivity()); } - /** - * Handles creation of the fragment view. - * - * @param inflater The inflater. - * @param container The view group containing the fragment. - * @param savedInstanceState The saved instance state. - * @return - */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -447,36 +442,15 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, } /** - * Centers the display view vertically for portrait orientation, and horizontally for - * lanscape orientations. The view is centered within the available space not occupied by - * the call card. + * Centers the display view vertically for portrait orientations. The view is centered within + * the available space not occupied by the call card. This is a no-op for landscape mode. * * @param displayVideo The video view to center. */ private void centerDisplayView(View displayVideo) { - // In a lansdcape layout we need to ensure we horizontally center the view based on whether - // the layout is left-to-right or right-to-left. - // In a left-to-right locale, the space for the video view is to the right of the call card - // so we need to translate it in the +X direction. - // In a right-to-left locale, the space for the video view is to the left of the call card - // so we need to translate it in the -X direction. - final boolean isLayoutRtl = InCallPresenter.isRtl(); - - ViewGroup.LayoutParams params = displayVideo.getLayoutParams(); - float spaceBesideCallCard = InCallPresenter.getInstance().getSpaceBesideCallCard(); - Log.d(this, "centerDisplayView: IsLandscape= " + mIsLandscape + " Layout width: " + - params.width + " height: " + params.height + " spaceBesideCallCard: " - + spaceBesideCallCard); - if (mIsLandscape) { - float videoViewTranslation = params.width / 2 - - spaceBesideCallCard / 2; - if (isLayoutRtl) { - displayVideo.setTranslationX(-videoViewTranslation); - } else { - displayVideo.setTranslationX(videoViewTranslation); - } - } else { - float videoViewTranslation = params.height / 2 + if (!mIsLandscape) { + float spaceBesideCallCard = InCallPresenter.getInstance().getSpaceBesideCallCard(); + float videoViewTranslation = displayVideo.getHeight() / 2 - spaceBesideCallCard / 2; displayVideo.setTranslationY(videoViewTranslation); } @@ -701,12 +675,6 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, } } - @Override - public boolean isActivityRestart() { - Log.d(this, "isActivityRestart " + mIsActivityRestart); - return mIsActivityRestart; - } - /** * @return {@code True} if the display video surface has been created. */ @@ -767,12 +735,12 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, params.height = height; preview.setLayoutParams(params); - int rotation = InCallPresenter.toRotationAngle(getCurrentRotation()); - int rotationAngle = 360 - rotation; - preview.setRotation(rotationAngle); - Log.d(this, "setPreviewSize: rotation=" + rotation + - " rotationAngle=" + rotationAngle); - + // The width and height are interchanged outside of this method based on the current + // orientation, so we can transform using "width", which will be either the width or + // the height. + Matrix transform = new Matrix(); + transform.setScale(-1, 1, width/2, 0); + preview.setTransform(transform); } } @@ -879,6 +847,19 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, } /** + * Determines the size of the preview surface. + * + * @return {@link Point} specifying the width and height of the preview surface. + */ + @Override + public Point getPreviewSize() { + if (sPreviewSurface == null) { + return null; + } + return sPreviewSurface.getSurfaceDimensions(); + } + + /** * Inflates the {@link ViewStub} containing the incoming and outgoing surfaces, if necessary, * and creates {@link VideoCallSurface} instances to track the surfaces. */ diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java index 43eef4b50..56f312f63 100644 --- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java +++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java @@ -65,7 +65,7 @@ import android.os.SystemProperties; * When downgrading to an audio-only video state, the {@code VideoCallPresenter} nulls both * surfaces. */ -public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi> implements +public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi> implements IncomingCallListener, InCallOrientationListener, InCallStateListener, InCallDetailsListener, SurfaceChangeListener, VideoEventListener, InCallVideoCallListenerNotifier.SessionModificationListener { @@ -167,7 +167,6 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi */ private int mCurrentCallSubstate; - /** Handler which resets request state to NO_REQUEST after an interval. */ private Handler mSessionModificationResetHandler; private static final long SESSION_MODIFICATION_RESET_DELAY_MS = 3000; @@ -339,11 +338,11 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi toggleFullScreen(); } - /** * Handles incoming calls. * - * @param state The in call state. + * @param oldState The old in call state. + * @param newState The new in call state. * @param call The call. */ @Override @@ -414,12 +413,14 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi + " hasVideoStateChanged=" + hasVideoStateChanged + " isVideoMode=" + isVideoMode()); - if (!hasVideoStateChanged) { return;} + if (!hasVideoStateChanged) { + return; + } updateCameraSelection(call); if (isVideoCall) { - enterVideoMode(call.getVideoCall(), call.getVideoState()); + enterVideoMode(call); } else if (isVideoMode()) { exitVideoMode(); } @@ -447,6 +448,9 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi if (!Objects.equals(prevCameraId, newCameraId) && CallUtils.isActiveVideoCall(call)) { enableCamera(call.getVideoCall(), true); } + + // Make sure we hide or show the video UI if needed. + showVideoUi(call.getVideoState(), call.getState()); } private void checkForCallSubstateChange(Call call) { @@ -487,7 +491,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi Log.d(this, "onPrimaryCallChanged: Entering video mode..."); updateCameraSelection(newPrimaryCall); - enterVideoMode(newPrimaryCall.getVideoCall(), newPrimaryCall.getVideoState()); + enterVideoMode(newPrimaryCall); } } @@ -538,6 +542,11 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi checkForVideoCallChange(call); checkForVideoStateChange(call); checkForCallStateChange(call); + checkForOrientationAllowedChange(call); + } + + private void checkForOrientationAllowedChange(Call call) { + InCallPresenter.getInstance().setInCallAllowsOrientationChange(CallUtils.isVideoCall(call)); } /** @@ -577,7 +586,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi } if (CallUtils.isVideoCall(call) && hasChanged) { - enterVideoMode(call.getVideoCall(), call.getVideoState()); + enterVideoMode(call); } } @@ -594,7 +603,10 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi * Enters video mode by showing the video surfaces and making other adjustments (eg. audio). * TODO(vt): Need to adjust size and orientation of preview surface here. */ - private void enterVideoMode(VideoCall videoCall, int newVideoState) { + private void enterVideoMode(Call call) { + VideoCall videoCall = call.getVideoCall(); + int newVideoState = call.getVideoState(); + Log.d(this, "enterVideoMode videoCall= " + videoCall + " videoState: " + newVideoState); VideoCallUi ui = getUi(); if (ui == null) { @@ -602,8 +614,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi return; } - showVideoUi(newVideoState); - InCallPresenter.getInstance().setInCallAllowsOrientationChange(true); + showVideoUi(newVideoState, call.getState()); // Communicate the current camera to telephony and make a request for the camera // capabilities. @@ -693,9 +704,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi private void exitVideoMode() { Log.d(this, "exitVideoMode"); - InCallPresenter.getInstance().setInCallAllowsOrientationChange(false); - - showVideoUi(VideoProfile.VideoState.AUDIO_ONLY); + showVideoUi(VideoProfile.VideoState.AUDIO_ONLY, Call.State.ACTIVE); enableCamera(mVideoCall, false); Log.d(this, "exitVideoMode mIsFullScreen: " + mIsFullScreen); @@ -707,21 +716,28 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi } /** - * Show video Ui depends on video state. + * Based on the current {@link VideoProfile.VideoState} and {@code CallState}, show or hide the + * incoming and outgoing video surfaces. The outgoing video surface is shown any time video + * is transmitting. The incoming video surface is shown whenever the video is un-paused and + * active. + * + * @param videoState The video state. + * @param callState The call state. */ - private void showVideoUi(int videoState) { + private void showVideoUi(int videoState, int callState) { VideoCallUi ui = getUi(); if (ui == null) { Log.e(this, "showVideoUi, VideoCallUi is null returning"); return; } - + boolean isPaused = VideoProfile.VideoState.isPaused(videoState); + boolean isCallActive = callState == Call.State.ACTIVE; if (VideoProfile.VideoState.isBidirectional(videoState)) { - ui.showVideoViews(true, true); + ui.showVideoViews(true, !isPaused && isCallActive); } else if (VideoProfile.VideoState.isTransmissionEnabled(videoState)) { ui.showVideoViews(true, false); } else if (VideoProfile.VideoState.isReceptionEnabled(videoState)) { - ui.showVideoViews(false, true); + ui.showVideoViews(false, !isPaused && isCallActive); } else { ui.hideVideoUi(); } @@ -818,6 +834,29 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi } mPreviewSurfaceState = PreviewSurfaceState.CAPABILITIES_RECEIVED; + changePreviewDimensions(width, height); + + // Check if the preview surface is ready yet; if it is, set it on the {@code VideoCall}. + // If it not yet ready, it will be set when when creation completes. + if (ui.isPreviewVideoSurfaceCreated()) { + mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET; + mVideoCall.setPreviewSurface(ui.getPreviewVideoSurface()); + } + } + + /** + * Changes the dimensions of the preview surface. + * + * @param width The new width. + * @param height The new height. + */ + private void changePreviewDimensions(int width, int height) { + VideoCallUi ui = getUi(); + if (ui == null) { + return; + } + + // Resize the surface used to display the preview video ui.setPreviewSurfaceSize(width, height); // Configure the preview surface to the correct aspect ratio. @@ -825,14 +864,10 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi if (width > 0 && height > 0) { aspectRatio = (float) width / (float) height; } - setPreviewSize(mDeviceOrientation, aspectRatio); - // Check if the preview surface is ready yet; if it is, set it on the {@code VideoCall}. - // If it not yet ready, it will be set when when creation completes. - if (ui.isPreviewVideoSurfaceCreated()) { - mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET; - mVideoCall.setPreviewSurface(ui.getPreviewVideoSurface()); - } + // Resize the textureview housing the preview video and rotate it appropriately based on + // the device orientation + setPreviewSize(mDeviceOrientation, aspectRatio); } /** @@ -874,8 +909,11 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi */ @Override public void onDeviceOrientationChanged(int orientation) { - Log.d(this, "onDeviceOrientationChanged: orientation=" + orientation); mDeviceOrientation = orientation; + Point previewDimensions = getUi().getPreviewSize(); + Log.d(this, "onDeviceOrientationChanged: orientation=" + orientation + " size: " + + previewDimensions); + changePreviewDimensions(previewDimensions.x, previewDimensions.y); } @Override @@ -939,6 +977,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi @Override public void onDowngradeToAudio(Call call) { + call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); // exit video mode exitVideoMode(); } @@ -1102,8 +1141,8 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi void setCallDataUsage(Context context, long dataUsage); void displayCallSessionEvent(int event); Point getScreenSize(); + Point getPreviewSize(); void cleanupSurfaces(); - boolean isActivityRestart(); void showCallSubstateChanged(int callSubstate); } } diff --git a/InCallUI/src/com/android/incallui/VideoPauseController.java b/InCallUI/src/com/android/incallui/VideoPauseController.java index c89b3b091..dd47c7f4a 100644 --- a/InCallUI/src/com/android/incallui/VideoPauseController.java +++ b/InCallUI/src/com/android/incallui/VideoPauseController.java @@ -38,12 +38,16 @@ import com.android.incallui.InCallVideoCallListenerNotifier.SessionModificationL import com.google.common.base.Preconditions; /** - * The class is responsible for generating video pause/resume request. + * This class is responsible for generating video pause/resume requests when the InCall UI is sent + * to the background and subsequently brought back to the foreground. */ class VideoPauseController implements InCallStateListener, IncomingCallListener, SessionModificationListener { - private static final String TAG = "VideoCallPauseController:"; + private static final String TAG = "VideoPauseController:"; + /** + * Keeps track of the current active/foreground call. + */ private class CallContext { public CallContext(Call call) { Preconditions.checkNotNull(call); @@ -53,7 +57,6 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, public void update(Call call) { mCall = Preconditions.checkNotNull(call); mState = call.getState(); - mId = call.getId(); mVideoState = call.getVideoState(); } @@ -61,17 +64,13 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, return mState; } - public String getId() { - return mId; - } - public int getVideoState() { return mVideoState; } public String toString() { - return String.format("CallContext {CallId=%s, State=%s, VideoState=", - mId, mState, mVideoState); + return String.format("CallContext {CallId=%s, State=%s, VideoState=%d}", + mCall.getId(), mState, mVideoState); } public Call getCall() { @@ -79,7 +78,6 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, } private int mState = State.INVALID; - private String mId; private int mVideoState; private Call mCall; } @@ -87,27 +85,21 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, private InCallPresenter mInCallPresenter; private static VideoPauseController sVideoPauseController; - private CallContext mPrimaryCallContext = null; // Context of primary call, if any. - private boolean mIsInBackground = false; // True if UI is not visible, false otherwise. - private int mVideoPauseMode = VIDEO_PAUSE_MODE_DISABLED; + /** + * The current call context, if applicable. + */ + private CallContext mPrimaryCallContext = null; /** - * Stores current video pause mode. - * 0 - Video Pause is disabled. - * 1 - Video Pause is enabled. + * Tracks whether the application is in the background. {@code True} if the application is in + * the background, {@code false} otherwise. */ - private static final String PROPERTY_VIDEO_PAUSE_MODE = "persist.radio.videopause.mode"; - private static int VIDEO_PAUSE_MODE_DISABLED = 0; - private static int VIDEO_PAUSE_MODE_ENABLED = 1; - - private VideoPauseController() { - mVideoPauseMode = SystemProperties.getInt(PROPERTY_VIDEO_PAUSE_MODE, - VIDEO_PAUSE_MODE_DISABLED); - if (mVideoPauseMode != VIDEO_PAUSE_MODE_ENABLED) { // Validate the mode before using. - mVideoPauseMode = VIDEO_PAUSE_MODE_DISABLED; - } - } + private boolean mIsInBackground = false; + /** + * Singleton accessor for the {@link VideoPauseController}. + * @return Singleton instance of the {@link VideoPauseController}. + */ /*package*/ static synchronized VideoPauseController getInstance() { if (sVideoPauseController == null) { @@ -116,23 +108,25 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, return sVideoPauseController; } + /** + * Configures the {@link VideoPauseController} to listen to call events. Configured via the + * {@link com.android.incallui.InCallPresenter}. + * + * @param inCallPresenter The {@link com.android.incallui.InCallPresenter}. + */ public void setUp(InCallPresenter inCallPresenter) { - if (!isVideoPausedEnabled()) { - return; - } - - log("setUp..."); + log("setUp"); mInCallPresenter = Preconditions.checkNotNull(inCallPresenter); mInCallPresenter.addListener(this); mInCallPresenter.addIncomingCallListener(this); InCallVideoCallListenerNotifier.getInstance().addSessionModificationListener(this); } + /** + * Cleans up the {@link VideoPauseController} by removing all listeners and clearing its + * internal state. Called from {@link com.android.incallui.InCallPresenter}. + */ public void tearDown() { - if (!isVideoPausedEnabled()) { - return; - } - log("tearDown..."); InCallVideoCallListenerNotifier.getInstance().removeSessionModificationListener(this); mInCallPresenter.removeListener(this); @@ -140,6 +134,9 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, clear(); } + /** + * Clears the internal state for the {@link VideoPauseController}. + */ private void clear() { mInCallPresenter = null; mPrimaryCallContext = null; @@ -147,8 +144,11 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, } /** - * The function gets called when call state changes. - * @param state Phone state. + * Handles changes in the {@link InCallState}. Triggers pause and resumption of video for the + * current foreground call. + * + * @param oldState The previous {@link InCallState}. + * @param newState The current {@link InCallState}. * @param callList List of current call. */ @Override @@ -179,7 +179,7 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, return; } - if (isOutgoing(mPrimaryCallContext) && canVideoPause && mIsInBackground) { + if (isDialing(mPrimaryCallContext) && canVideoPause && mIsInBackground) { // Bring UI to foreground if outgoing request becomes active while UI is in // background. bringToForeground(); @@ -192,6 +192,15 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, updatePrimaryCallContext(call); } + /** + * Handles a change to the primary call. + * <p> + * Reject incoming or hangup dialing call: Where the previous call was an incoming call or a + * call in dialing state, resume the new primary call. + * Call swap: Where the new primary call is incoming, pause video on the previous primary call. + * + * @param call The new primary call. + */ private void onPrimaryCallChanged(Call call) { log("onPrimaryCallChanged: New call = " + call); log("onPrimaryCallChanged: Old call = " + mPrimaryCallContext); @@ -200,25 +209,26 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, Preconditions.checkState(!areSame(call, mPrimaryCallContext)); final boolean canVideoPause = CallUtils.canVideoPause(call); - if (isWaitingCall(mPrimaryCallContext) && canVideoPause && !mIsInBackground) { - // Send resume request for the active call, if user rejects incoming - // call and UI is in foreground. + if ((isIncomingCall(mPrimaryCallContext) || isDialing(mPrimaryCallContext)) + && canVideoPause && !mIsInBackground) { + // Send resume request for the active call, if user rejects incoming call or ends + // dialing call and UI is in the foreground. sendRequest(call, true); - } else if (isWaitingCall(call) && canVideoPause(mPrimaryCallContext)) { + } else if (isIncomingCall(call) && canVideoPause(mPrimaryCallContext)) { // Send pause request if there is an active video call, and we just received a new // incoming call. sendRequest(mPrimaryCallContext.getCall(), false); - } else if (isOutgoing(mPrimaryCallContext) && canVideoPause && !mIsInBackground) { - // Send resume request for the active call, if user ends outgoing call - // and UI is in foreground. - sendRequest(call, true); } updatePrimaryCallContext(call); } /** - * The function gets called when InCallUI receives a new incoming call. + * Handles new incoming calls by triggering a change in the primary call. + * + * @param oldState the old {@link InCallState}. + * @param newState the new {@link InCallState}. + * @param call the incoming call. */ @Override public void onIncomingCall(InCallState oldState, InCallState newState, Call call) { @@ -231,6 +241,11 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, onPrimaryCallChanged(call); } + /** + * Caches a reference to the primary call and stores its previous state. + * + * @param call The new primary call. + */ private void updatePrimaryCallContext(Call call) { if (call == null) { mPrimaryCallContext = null; @@ -246,43 +261,67 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, * @param showing true if UI is in the foreground, false otherwise. */ public void onUiShowing(boolean showing) { - if (!isVideoPausedEnabled() || mInCallPresenter == null) { + // Only send pause/unpause requests if we are in the INCALL state. + if (mInCallPresenter == null || mInCallPresenter.getInCallState() != InCallState.INCALL) { return; } - final boolean notify = mInCallPresenter.getInCallState() == InCallState.INCALL; if (showing) { - onResume(notify); + onResume(); } else { - onPause(notify); + onPause(); } } + /** + * Handles requests to upgrade to video. + * + * @param call The call the request was received for. + * @param videoState The video state that the request wants to upgrade to. + */ @Override public void onUpgradeToVideoRequest(Call call, int videoState) { + // Not used. } + /** + * Handles successful upgrades to video. + * @param call The call the request was successful for. + */ @Override public void onUpgradeToVideoSuccess(Call call) { + // Not used. } + /** + * Handles a failure to upgrade a call to video. + * + * @param status The failure status. + * @param call The call the request was successful for. + */ @Override public void onUpgradeToVideoFail(int status, Call call) { // TODO (ims-vt) Automatically bring in call ui to foreground. } + /** + * Handles a downgrade of a call to audio-only. + * + * @param call The call which was downgraded to audio-only. + */ @Override public void onDowngradeToAudio(Call call) { } /** - * Called when UI becomes visible. This will send resume request for current video call, if any. + * Called when UI is brought to the foreground. Sends a session modification request to resume + * the outgoing video. */ - private void onResume(boolean notify) { - log("onResume: notify=" + notify); + private void onResume() { + log("onResume"); mIsInBackground = false; - if (canVideoPause(mPrimaryCallContext) && notify) { + if (canVideoPause(mPrimaryCallContext)) { sendRequest(mPrimaryCallContext.getCall(), true); } else { log("onResume. Ignoring..."); @@ -290,13 +329,14 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, } /** - * Called when UI becomes invisible. This will send pause request for current video call, if any. + * Called when UI is sent to the background. Sends a session modification request to pause the + * outgoing video. */ - private void onPause(boolean notify) { - log("onPause: notify=" + notify); + private void onPause() { + log("onPause"); mIsInBackground = true; - if (canVideoPause(mPrimaryCallContext) && notify) { + if (canVideoPause(mPrimaryCallContext)) { sendRequest(mPrimaryCallContext.getCall(), false); } else { log("onPause, Ignoring..."); @@ -314,75 +354,118 @@ class VideoPauseController implements InCallStateListener, IncomingCallListener, /** * Sends Pause/Resume request. + * * @param call Call to be paused/resumed. * @param resume If true resume request will be sent, otherwise pause request. */ private void sendRequest(Call call, boolean resume) { + // Check if this call supports pause/un-pause. + if (!call.can(android.telecom.Call.Details.CAPABILITY_CAN_PAUSE_VIDEO)) { + return; + } + if (resume) { log("sending resume request, call=" + call); - call.getVideoCall().sendSessionModifyRequest(CallUtils.makeVideoUnPauseProfile(call)); + call.getVideoCall() + .sendSessionModifyRequest(CallUtils.makeVideoUnPauseProfile(call)); } else { log("sending pause request, call=" + call); call.getVideoCall().sendSessionModifyRequest(CallUtils.makeVideoPauseProfile(call)); } } - private boolean isVideoPausedEnabled() { - return mVideoPauseMode != VIDEO_PAUSE_MODE_DISABLED; - } - + /** + * Determines if a given call is the same one stored in a {@link CallContext}. + * + * @param call The call. + * @param callContext The call context. + * @return {@code true} if the {@link Call} is the same as the one referenced in the + * {@link CallContext}. + */ private static boolean areSame(Call call, CallContext callContext) { if (call == null && callContext == null) { return true; } else if (call == null || callContext == null) { return false; } - return call.getId().equals(callContext.getId()); - } - - private static boolean areSame(CallContext callContext, Call call) { - return areSame(call, callContext); + return call.equals(callContext.getCall()); } + /** + * Determines if a video call can be paused. Only a video call which is active can be paused. + * + * @param callContext The call context to check. + * @return {@code true} if the call is an active video call. + */ private static boolean canVideoPause(CallContext callContext) { return isVideoCall(callContext) && callContext.getState() == Call.State.ACTIVE; } + /** + * Determines if a call referenced by a {@link CallContext} is a video call. + * + * @param callContext The call context. + * @return {@code true} if the call is a video call, {@code false} otherwise. + */ private static boolean isVideoCall(CallContext callContext) { return callContext != null && VideoProfile.VideoState.isVideo(callContext.getVideoState()); } /** - * Returns true if call is in incoming/waiting state, false otherwise. + * Determines if call is in incoming/waiting state. + * + * @param call The call context. + * @return {@code true} if the call is in incoming or waiting state, {@code false} otherwise. */ - private static boolean isWaitingCall(CallContext call) { - return call != null && (call.getState() == Call.State.CALL_WAITING - || call.getState() == Call.State.INCOMING); + private static boolean isIncomingCall(CallContext call) { + return call != null && isIncomingCall(call.getCall()); } - private static boolean isWaitingCall(Call call) { + /** + * Determines if a call is in incoming/waiting state. + * + * @param call The call. + * @return {@code true} if the call is in incoming or waiting state, {@code false} otherwise. + */ + private static boolean isIncomingCall(Call call) { return call != null && (call.getState() == Call.State.CALL_WAITING || call.getState() == Call.State.INCOMING); } /** - * Returns true if the call is outgoing, false otherwise + * Determines if a call is dialing. + * + * @param call The call context. + * @return {@code true} if the call is dialing, {@code false} otherwise. */ - private static boolean isOutgoing(CallContext call) { + private static boolean isDialing(CallContext call) { return call != null && Call.State.isDialing(call.getState()); } /** - * Returns true if the call is on hold, false otherwise + * Determines if a call is holding. + * + * @param call The call context. + * @return {@code true} if the call is holding, {@code false} otherwise. */ private static boolean isHolding(CallContext call) { return call != null && call.getState() == Call.State.ONHOLD; } + /** + * Logs a debug message. + * + * @param msg The message. + */ private void log(String msg) { Log.d(this, TAG + msg); } + /** + * Logs an error message. + * + * @param msg The message. + */ private void loge(String msg) { Log.e(this, TAG + msg); } |