summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Bird <sbird@cyngn.com>2016-05-02 09:39:57 -0700
committerRajesh Yengisetty <rajesh@cyngn.com>2016-05-09 09:42:17 -0700
commitf7c888b58697724cfdb5b4663aa0beb67f646249 (patch)
treeab6237eb6656c684f9ded592bacd89ef786e9504
parent9c3fbd481889746441dd14b5a29a3d3db562a4cc (diff)
downloadandroid_packages_apps_InCallUI-f7c888b58697724cfdb5b4663aa0beb67f646249.tar.gz
android_packages_apps_InCallUI-f7c888b58697724cfdb5b4663aa0beb67f646249.tar.bz2
android_packages_apps_InCallUI-f7c888b58697724cfdb5b4663aa0beb67f646249.zip
Move Mod plugins out of CallButtonFragment to ModButtonFragment
Things were getting crowded. Add a new button bar explicitly for plugins. Tickets CD-592 & NOTES-101 Change-Id: I1bdf074d8e3417161eb29cf05140016b084c5006 (cherry picked from commit 1513c75c14068c3a43f66ffa2431572896c76c96)
-rw-r--r--res/color/mod_icon_tint.xml24
-rw-r--r--res/drawable/btn_mod_background.xml33
-rw-r--r--res/drawable/btn_mod_drawable.xml31
-rw-r--r--res/drawable/btn_mod_unselected.xml25
-rw-r--r--res/drawable/ic_video.xml14
-rw-r--r--res/layout-land/call_card_fragment.xml20
-rw-r--r--res/layout/call_card_fragment.xml5
-rw-r--r--res/layout/mod_button_fragment.xml62
-rw-r--r--res/values-land/cm_dimens.xml21
-rw-r--r--res/values/cm_colors.xml2
-rw-r--r--res/values/cm_dimens.xml4
-rw-r--r--res/values/cm_strings.xml1
-rw-r--r--res/values/cm_styles.xml30
-rw-r--r--src/com/android/incallui/CallCardFragment.java6
-rw-r--r--src/com/android/incallui/InCallActivity.java3
-rw-r--r--src/com/android/incallui/ModButtonFragment.java450
-rw-r--r--src/com/android/incallui/ModButtonPresenter.java428
17 files changed, 1153 insertions, 6 deletions
diff --git a/res/color/mod_icon_tint.xml b/res/color/mod_icon_tint.xml
new file mode 100644
index 00000000..37138002
--- /dev/null
+++ b/res/color/mod_icon_tint.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@color/button_disabled_color" android:state_enabled="false" />
+ <item android:color="@color/incall_accent_color" android:state_selected="true" />
+ <item android:color="@color/incall_accent_color" android:state_pressed="true" />
+ <item android:color="@color/button_default_color" />
+</selector>
+
diff --git a/res/drawable/btn_mod_background.xml b/res/drawable/btn_mod_background.xml
new file mode 100644
index 00000000..e212d4d8
--- /dev/null
+++ b/res/drawable/btn_mod_background.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Background resource for "compound buttons" in the in-call UI.
+ These buttons have two states (checked and unchecked), and
+ show a blue bar along the bottom edge when checked. -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/incall_accent_color">
+ <item>
+ <selector>
+
+ <item android:state_focused="true"
+ android:drawable="@drawable/btn_unselected_focused" />
+
+ <item android:drawable="@drawable/btn_mod_unselected" />
+
+ </selector>
+ </item>
+</ripple>
diff --git a/res/drawable/btn_mod_drawable.xml b/res/drawable/btn_mod_drawable.xml
new file mode 100644
index 00000000..d69887d0
--- /dev/null
+++ b/res/drawable/btn_mod_drawable.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2016 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/backgroundItem"
+ android:drawable="@drawable/btn_mod_background" />
+
+ <item android:id="@+id/foregroundItem"
+ android:top="@dimen/mod_button_inset"
+ android:right="@dimen/mod_button_inset"
+ android:bottom="@dimen/mod_button_inset"
+ android:left="@dimen/mod_button_inset"
+ android:drawable="@drawable/ic_video"/>
+
+</layer-list> \ No newline at end of file
diff --git a/res/drawable/btn_mod_unselected.xml b/res/drawable/btn_mod_unselected.xml
new file mode 100644
index 00000000..eaff307f
--- /dev/null
+++ b/res/drawable/btn_mod_unselected.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2016 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="@color/mod_button_background_color" />
+ </shape>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/res/drawable/ic_video.xml b/res/drawable/ic_video.xml
new file mode 100644
index 00000000..9b23dcb9
--- /dev/null
+++ b/res/drawable/ic_video.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M0 0h24v24H0z" />
+ <path
+ android:fillColor="#000000"
+ android:pathData="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55 .45 1 1 1h12c.55 0 1-.45
+1-1v-3.5l4 4v-11l-4 4z" />
+</vector>
diff --git a/res/layout-land/call_card_fragment.xml b/res/layout-land/call_card_fragment.xml
index dcdd1aa1..8109c33f 100644
--- a/res/layout-land/call_card_fragment.xml
+++ b/res/layout-land/call_card_fragment.xml
@@ -77,18 +77,26 @@
<include layout="@layout/primary_call_info" />
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent" >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="bottom"
+ android:clipChildren="false"
+ android:clipToPadding="false" >
<fragment android:name="com.android.incallui.CallButtonFragment"
android:id="@+id/callButtonFragment"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <fragment android:name="com.android.incallui.ModButtonFragment"
+ android:id="@+id/modButtonFragment"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="bottom"
android:layout_marginBottom="@dimen/call_buttons_bottom_margin" />
- </FrameLayout>
+ </LinearLayout>
</LinearLayout>
diff --git a/res/layout/call_card_fragment.xml b/res/layout/call_card_fragment.xml
index ef40b1f4..d5d6e421 100644
--- a/res/layout/call_card_fragment.xml
+++ b/res/layout/call_card_fragment.xml
@@ -72,6 +72,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+ <fragment android:name="com.android.incallui.ModButtonFragment"
+ android:id="@+id/modButtonFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<TextView android:id="@+id/connectionServiceMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/layout/mod_button_fragment.xml b/res/layout/mod_button_fragment.xml
new file mode 100644
index 00000000..ab96220e
--- /dev/null
+++ b/res/layout/mod_button_fragment.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- In-call Plugins touch UI elements, used on some platforms.
+ This layout is modeled after call_button_fragment -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/bottomModButtons"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_margin="0dp"
+ android:padding="0dp"
+ android:background="@color/mod_button_background_color"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:animateLayoutChanges="true" >
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="bottom|center_vertical"
+ android:layout_marginLeft="@dimen/mod_bar_margin_sides"
+ android:layout_marginRight="@dimen/mod_bar_margin_sides"
+ android:baselineAligned="true">
+
+ <ImageButton android:id="@+id/inCallProviders"
+ style="@style/ModButton"
+ android:background="@drawable/btn_mod_drawable"
+ android:contentDescription="@string/onscreenTransferCall"
+ android:visibility="gone" />
+
+ <!-- Take a note for this call -->
+ <ImageButton android:id="@+id/takeNoteButton"
+ style="@style/ModButton"
+ android:background="@drawable/btn_mod_drawable"
+ android:contentDescription="@string/onscreenNoteText"
+ android:visibility="gone" />
+
+ <!-- "Overflow" -->
+ <ImageButton android:id="@+id/overflowButton"
+ style="@style/ModButton"
+ android:background="@drawable/btn_overflow"
+ android:contentDescription="@string/onscreenModOverflowText"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/values-land/cm_dimens.xml b/res/values-land/cm_dimens.xml
new file mode 100644
index 00000000..c091884d
--- /dev/null
+++ b/res/values-land/cm_dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <dimen name="mod_button_inset">8dp</dimen>
+ <dimen name="mod_bar_margin_sides">8dp</dimen>
+</resources>
diff --git a/res/values/cm_colors.xml b/res/values/cm_colors.xml
index 4b3f214d..2769a312 100644
--- a/res/values/cm_colors.xml
+++ b/res/values/cm_colors.xml
@@ -33,4 +33,6 @@
<color name="incoming_call_hold_small_circle">#607d8b</color>
<color name="incoming_call_hold_bitmap">#b2ffffff</color>
<color name="incoming_call_sms_circle">#0288d1</color>
+
+ <color name="mod_button_background_color">@color/dialer_theme_color_dark</color>
</resources> \ No newline at end of file
diff --git a/res/values/cm_dimens.xml b/res/values/cm_dimens.xml
index b73349cf..4807b61d 100644
--- a/res/values/cm_dimens.xml
+++ b/res/values/cm_dimens.xml
@@ -49,4 +49,8 @@
<dimen name="incoming_call_widget_small_circle_left_offset">30dp</dimen>
<dimen name="incoming_call_widget_display_text_side_offset">30dp</dimen>
+
+ <dimen name="mod_button_inset">12dp</dimen>
+ <dimen name="mod_button_margin_bottom">0dp</dimen>
+ <dimen name="mod_bar_margin_sides">30dp</dimen>
</resources>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index 18d3a773..03eac4bf 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -86,4 +86,5 @@
<string name="display_text_target_answer_and_hold_active">HOLD CURRENT CALL AND ANSWER</string>
<string name="display_text_target_answer_and_end_active">END CURRENT CALL AND ANSWER</string>
+ <string name="onscreenModOverflowText">More Mods</string>
</resources>
diff --git a/res/values/cm_styles.xml b/res/values/cm_styles.xml
new file mode 100644
index 00000000..2c563d82
--- /dev/null
+++ b/res/values/cm_styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+ <!-- Buttons in the main "mod button row" of the in-call onscreen touch UI. -->
+ <style name="ModButton">
+ <item name="android:layout_width">@dimen/in_call_button_dimension</item>
+ <item name="android:layout_height">@dimen/in_call_button_dimension</item>
+ <item name="android:layout_marginBottom">@dimen/mod_button_margin_bottom</item>
+ <item name="android:layout_marginLeft">@dimen/call_button_margin_horizontal</item>
+ <item name="android:layout_marginRight">@dimen/call_button_margin_horizontal</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ </style>
+
+</resources>
diff --git a/src/com/android/incallui/CallCardFragment.java b/src/com/android/incallui/CallCardFragment.java
index 498d13aa..c64f200b 100644
--- a/src/com/android/incallui/CallCardFragment.java
+++ b/src/com/android/incallui/CallCardFragment.java
@@ -132,6 +132,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
// Container view that houses the primary call information
private ViewGroup mPrimaryCallInfo;
private View mCallButtonsContainer;
+ private View mModButtonsContainer;
private TextView mRecordingTimeLabel;
private TextView mRecordingIcon;
@@ -284,6 +285,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
mPrimaryCallCardContainer = view.findViewById(R.id.primary_call_info_container);
mPrimaryCallInfo = (ViewGroup) view.findViewById(R.id.primary_call_banner);
mCallButtonsContainer = view.findViewById(R.id.callButtonFragment);
+ mModButtonsContainer = view.findViewById(R.id.modButtonFragment);
mInCallMessageLabel = (TextView) view.findViewById(R.id.connectionServiceMessage);
mProgressSpinner = view.findViewById(R.id.progressSpinner);
@@ -1225,6 +1227,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
mPrimaryCallCardContainer.setBackgroundColor(themeColors.mPrimaryColor);
}
mCallButtonsContainer.setBackgroundColor(themeColors.mPrimaryColor);
+ mModButtonsContainer.setBackgroundColor(themeColors.mSecondaryColor);
mCallSubject.setTextColor(themeColors.mPrimaryColor);
mCurrentThemeColors = themeColors;
@@ -1272,6 +1275,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
mFloatingActionButtonController.setScreenWidth(parent.getWidth());
mCallButtonsContainer.setAlpha(0);
+ mModButtonsContainer.setAlpha(0);
mCallStateLabel.setAlpha(0);
mPrimaryName.setAlpha(0);
mCallTypeLabel.setAlpha(0);
@@ -1283,6 +1287,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
assignTranslateAnimation(mCallNumberAndLabel, 3);
assignTranslateAnimation(mCallTypeLabel, 4);
assignTranslateAnimation(mCallButtonsContainer, 5);
+ assignTranslateAnimation(mModButtonsContainer, 6);
final Animator animator = getShrinkAnimator(parent.getHeight(), originalHeight);
@@ -1480,6 +1485,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
private void setViewStatePostAnimation(OnLayoutChangeListener layoutChangeListener) {
setViewStatePostAnimation(mCallButtonsContainer);
+ setViewStatePostAnimation(mModButtonsContainer);
setViewStatePostAnimation(mCallStateLabel);
setViewStatePostAnimation(mPrimaryName);
setViewStatePostAnimation(mCallTypeLabel);
diff --git a/src/com/android/incallui/InCallActivity.java b/src/com/android/incallui/InCallActivity.java
index d5c5db8f..4d1cee5b 100644
--- a/src/com/android/incallui/InCallActivity.java
+++ b/src/com/android/incallui/InCallActivity.java
@@ -93,6 +93,7 @@ public class InCallActivity extends Activity implements FragmentDisplayManager {
private static final int SNACKBAR_TIMEOUT = 10000; // 10 seconds auto dismiss
private CallButtonFragment mCallButtonFragment;
+ private ModButtonFragment mModButtonFragment;
private CallCardFragment mCallCardFragment;
private AnswerFragment mAnswerFragment;
private DialpadFragment mDialpadFragment;
@@ -371,6 +372,8 @@ public class InCallActivity extends Activity implements FragmentDisplayManager {
mConferenceManagerFragment = (ConferenceManagerFragment) fragment;
} else if (fragment instanceof CallButtonFragment) {
mCallButtonFragment = (CallButtonFragment) fragment;
+ } else if (fragment instanceof ModButtonFragment) {
+ mModButtonFragment = (ModButtonFragment) fragment;
}
}
diff --git a/src/com/android/incallui/ModButtonFragment.java b/src/com/android/incallui/ModButtonFragment.java
new file mode 100644
index 00000000..e3cda71c
--- /dev/null
+++ b/src/com/android/incallui/ModButtonFragment.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.incallui;
+
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+import com.android.incallui.incallapi.InCallPluginInfo;
+
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.StateListDrawable;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.SparseIntArray;
+import android.view.ContextThemeWrapper;
+import android.view.HapticFeedbackConstants;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.PopupMenu;
+import android.widget.PopupMenu.OnMenuItemClickListener;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.incallui.ModButtonFragment.Buttons.BUTTON_COUNT;
+import static com.android.incallui.ModButtonFragment.Buttons.BUTTON_INCALL;
+import static com.android.incallui.ModButtonFragment.Buttons.BUTTON_TAKE_NOTE;
+
+/**
+ * Fragment for mod control buttons
+ */
+public class ModButtonFragment
+ extends BaseFragment<ModButtonPresenter, ModButtonPresenter.ModButtonUi>
+ implements ModButtonPresenter.ModButtonUi,
+ View.OnClickListener {
+
+ private static final String TAG = ModButtonFragment.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private int mButtonMaxVisible;
+ // 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;
+
+ private SparseIntArray mButtonVisibilityMap = new SparseIntArray(BUTTON_COUNT);
+
+ private ImageButton mInCallProvider;
+ private ImageButton mTakeNoteButton;
+ private ImageButton mOverflowButton;
+
+ private PopupMenu mOverflowPopup;
+
+ private MaterialPalette mCurrentThemeColors;
+
+ public interface Buttons {
+ int BUTTON_INCALL = 0;
+ int BUTTON_TAKE_NOTE = 1;
+ int BUTTON_COUNT = 2;
+ }
+
+ @Override
+ public ModButtonPresenter createPresenter() {
+ return new ModButtonPresenter();
+ }
+
+ @Override
+ public ModButtonPresenter.ModButtonUi getUi() {
+ return this;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ for (int i = 0; i < BUTTON_COUNT; i++) {
+ mButtonVisibilityMap.put(i, BUTTON_HIDDEN);
+ }
+
+ mButtonMaxVisible = getResources().getInteger(R.integer.call_card_max_buttons);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View parent = inflater.inflate(R.layout.mod_button_fragment, container, false);
+
+ mInCallProvider = (ImageButton) parent.findViewById(R.id.inCallProviders);
+ mInCallProvider.setOnClickListener(this);
+ mTakeNoteButton = (ImageButton) parent.findViewById(R.id.takeNoteButton);
+ mTakeNoteButton.setOnClickListener(this);
+ mOverflowButton = (ImageButton) parent.findViewById(R.id.overflowButton);
+ mOverflowButton.setOnClickListener(this);
+
+ return parent;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ updateColors();
+ }
+
+ @Override
+ public void onClick(View view) {
+ int id = view.getId();
+ if (DEBUG) Log.d(this, "onClick(View " + view + ", id " + id + ")...");
+
+ switch(id) {
+ case R.id.inCallProviders:
+ getPresenter().switchToVideoCall();
+ break;
+ case R.id.takeNoteButton:
+ getPresenter().takeNote();
+ break;
+ case R.id.overflowButton:
+ if (mOverflowPopup != null) {
+ mOverflowPopup.show();
+ }
+ break;
+ default:
+ Log.wtf(this, "onClick: unexpected");
+ return;
+ }
+
+ view.performHapticFeedback(
+ HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ }
+
+ public void updateColors() {
+ MaterialPalette themeColors = InCallPresenter.getInstance().getThemeColors();
+
+ if (mCurrentThemeColors != null && mCurrentThemeColors.equals(themeColors)) {
+ return;
+ }
+
+ mCurrentThemeColors = themeColors;
+
+ ImageButton[] normalButtons = {
+ mInCallProvider,
+ mTakeNoteButton,
+ mOverflowButton
+ };
+
+ for (ImageButton button : normalButtons) {
+ final LayerDrawable layers = (LayerDrawable) button.getBackground();
+ final RippleDrawable btnDrawable = backgroundDrawable(themeColors);
+ layers.setDrawableByLayerId(R.id.backgroundItem, btnDrawable);
+ }
+
+ updateButtonStates();
+ }
+
+ /**
+ * Generate a RippleDrawable which will be the background of a button to ensure it
+ * is the same color as the rest of the call card.
+ */
+ private RippleDrawable backgroundDrawable(MaterialPalette palette) {
+ Resources res = getResources();
+ ColorStateList rippleColor =
+ ColorStateList.valueOf(res.getColor(R.color.incall_accent_color));
+
+ StateListDrawable stateListDrawable = new StateListDrawable();
+ addFocused(res, stateListDrawable);
+ addUnselected(res, stateListDrawable, palette);
+
+ return new RippleDrawable(rippleColor, stateListDrawable, null);
+ }
+
+ // state_focused
+ private void addFocused(Resources res, StateListDrawable drawable) {
+ int[] focused = {android.R.attr.state_focused};
+ Drawable focusedDrawable = res.getDrawable(R.drawable.btn_unselected_focused);
+ drawable.addState(focused, focusedDrawable);
+ }
+
+ // default
+ private void addUnselected(Resources res, StateListDrawable drawable, MaterialPalette palette) {
+ LayerDrawable unselectedDrawable =
+ (LayerDrawable) res.getDrawable(R.drawable.btn_mod_unselected);
+ ((GradientDrawable) unselectedDrawable.getDrawable(0)).setColor(palette.mSecondaryColor);
+ drawable.addState(new int[0], unselectedDrawable);
+ }
+
+
+ @Override
+ public void setEnabled(boolean isEnabled) {
+ mInCallProvider.setEnabled(isEnabled);
+ mTakeNoteButton.setEnabled(isEnabled);
+ mOverflowButton.setEnabled(isEnabled);
+ }
+
+ @Override
+ public void showButton(int buttonId, boolean show) {
+ mButtonVisibilityMap.put(buttonId, show ? BUTTON_VISIBLE : BUTTON_HIDDEN);
+ }
+
+ @Override
+ public void enableButton(int buttonId, boolean enable) {
+ final View button = getButtonById(buttonId);
+ if (button != null) {
+ button.setEnabled(enable);
+ }
+ }
+
+ private View getButtonById(int id) {
+ switch (id) {
+ case BUTTON_INCALL:
+ return mInCallProvider;
+ case BUTTON_TAKE_NOTE:
+ return mTakeNoteButton;
+ default:
+ Log.w(this, "Invalid button id");
+ return null;
+ }
+ }
+
+ 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 ModButtonPresenter. If there are more visible buttons than
+ * the allowed maximum, the excess buttons are collapsed into a single overflow menu.
+ */
+ @Override
+ 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 (button == null) {
+ continue;
+ }
+ if (visibility == BUTTON_VISIBLE) {
+ visibleCount++;
+ if (visibleCount <= mButtonMaxVisible) {
+ 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) {
+ final int id = item.getItemId();
+ getButtonById(id).performClick();
+ return true;
+ }
+ });
+ }
+ }
+
+ @Override
+ public void setDeepLinkNoteIcon(Drawable d) {
+ if (d == null) {
+ mTakeNoteButton.setVisibility(View.GONE);
+ } else {
+ createLayers(mTakeNoteButton, d);
+ }
+ }
+
+ @Override
+ public void modifyChangeToVideoButton() {
+ List<InCallPluginInfo> contactInCallPlugins
+ = getPresenter().getContactInCallPluginInfoList();
+ int listSize = (contactInCallPlugins != null) ? contactInCallPlugins.size() : 0;
+ if (listSize == 1) {
+ InCallPluginInfo info = contactInCallPlugins.get(0);
+ if (info != null && info.getPluginVideoIcon() != null) {
+ createLayers(mInCallProvider, info.getPluginVideoIcon());
+ }
+ } else {
+ createLayers(mInCallProvider, getResources().getDrawable(R.drawable.ic_video));
+ }
+ }
+
+ /**The function is called when Video Call button gets pressed. The function creates and
+ * displays video call options.
+ */
+ @Override
+ public void displayVideoCallProviderOptions() {
+ ModButtonPresenter.ModButtonUi ui = getUi();
+ if (ui == null) {
+ Log.e(this, "Cannot display VideoCallOptions as ui is null");
+ return;
+ }
+
+ Context context = getContext();
+
+ final ArrayList<Drawable> icons = new ArrayList<Drawable>();
+ final ArrayList<String> items = new ArrayList<String>();
+ final ArrayList<Integer> itemToCallType = new ArrayList<Integer>();
+
+ // Prepare the string array and mapping.
+ List<InCallPluginInfo> contactInCallPlugins =
+ getPresenter().getContactInCallPluginInfoList();
+ if (contactInCallPlugins != null && !contactInCallPlugins.isEmpty()) {
+ int i = 0;
+ for (InCallPluginInfo info : contactInCallPlugins) {
+ items.add(info.getPluginTitle());
+ icons.add(info.getPluginBrandIcon());
+ itemToCallType.add(i);
+ i++;
+ }
+ }
+
+ ListAdapter adapter = new ListItemWithImageArrayAdapter(context.getApplicationContext(),
+ R.layout.videocall_handoff_item, items, icons);
+ DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ final int selCallType = itemToCallType.get(item);
+ // InCall Plugin selected
+ getPresenter().handoverCallToVoIPPlugin(selCallType);
+ dialog.dismiss();
+ }
+ };
+ AlertDialog.Builder builder = new AlertDialog.Builder(getUi().getContext());
+ builder.setTitle(R.string.video_call_option_title);
+ builder.setAdapter(adapter, listener);
+ final AlertDialog alert;
+ alert = builder.create();
+ alert.show();
+ }
+
+ @Override
+ public Context getContext() {
+ return getActivity();
+ }
+
+ @Override
+ public void showInviteSnackbar(final PendingIntent inviteIntent, String inviteText) {
+ if (TextUtils.isEmpty(inviteText)) {
+ return;
+ }
+ final InCallActivity activity = (InCallActivity) getActivity();
+ if (activity != null) {
+ activity.showInviteSnackbar(inviteIntent, inviteText);
+ }
+ }
+
+ /**
+ * Adapter used to Array adapter with an icon and custom item layout
+ */
+ private class ListItemWithImageArrayAdapter extends ArrayAdapter<String> {
+ private int mLayout;
+ private List<Drawable> mIcons;
+
+ public ListItemWithImageArrayAdapter(Context context, int layout, List<String> titles,
+ List<Drawable> icons) {
+ super(context, 0, titles);
+ mLayout = layout;
+ mIcons = icons;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ String title = getItem(position);
+ Drawable icon = mIcons.get(position);
+ if (convertView == null) {
+ convertView = LayoutInflater.from(getContext()).inflate(mLayout, parent, false);
+ }
+
+ TextView textView = (TextView) convertView.findViewById(R.id.title);
+ textView.setText(title);
+
+ ImageView msgIcon = (ImageView) convertView.findViewById(R.id.icon);
+ msgIcon.setImageDrawable(icon);
+
+ return convertView;
+ }
+ }
+
+ private void createLayers(ImageButton button, Drawable icon) {
+ Drawable newIcon = icon.getConstantState().newDrawable().mutate();
+
+ final LayerDrawable layerDrawable =
+ (LayerDrawable) getResources().getDrawable(R.drawable.btn_mod_drawable).mutate();
+
+ newIcon.setTintList(getResources().getColorStateList(R.color.mod_icon_tint));
+ newIcon.setAutoMirrored(false);
+
+ layerDrawable.setDrawableByLayerId(R.id.foregroundItem, newIcon);
+ button.setBackgroundDrawable(layerDrawable);
+ }
+}
diff --git a/src/com/android/incallui/ModButtonPresenter.java b/src/com/android/incallui/ModButtonPresenter.java
new file mode 100644
index 00000000..df350f54
--- /dev/null
+++ b/src/com/android/incallui/ModButtonPresenter.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.incallui;
+
+import com.android.dialer.deeplink.DeepLinkIntegrationManager;
+import com.android.incallui.ContactInfoCache.ContactCacheEntry;
+import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
+import com.android.incallui.InCallPresenter.CanAddCallListener;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.android.incallui.InCallPresenter.InCallPluginUpdateListener;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.InCallPresenter.IncomingCallListener;
+import com.android.incallui.incallapi.InCallPluginInfo;
+import com.android.phone.common.ambient.AmbientConnection;
+import com.android.phone.common.incall.StartInCallCallReceiver;
+import com.cyanogen.ambient.common.api.AmbientApiClient;
+import com.cyanogen.ambient.common.api.ResultCallback;
+import com.cyanogen.ambient.deeplink.DeepLink;
+import com.cyanogen.ambient.deeplink.applicationtype.DeepLinkApplicationType;
+import com.cyanogen.ambient.deeplink.linkcontent.CallDeepLinkContent;
+import com.cyanogen.ambient.deeplink.linkcontent.DeepLinkContentType;
+import com.cyanogen.ambient.incall.InCallServices;
+import com.cyanogen.ambient.incall.extension.OriginCodes;
+import com.cyanogen.ambient.incall.extension.StartCallRequest;
+import com.cyanogen.ambient.incall.extension.StatusCodes;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import java.util.List;
+
+import cyanogenmod.providers.CMSettings;
+
+import static com.android.incallui.ModButtonFragment.Buttons.BUTTON_INCALL;
+import static com.android.incallui.ModButtonFragment.Buttons.BUTTON_TAKE_NOTE;
+
+/**
+ * Logic for mod buttons.
+ */
+public class ModButtonPresenter extends Presenter<ModButtonPresenter.ModButtonUi>
+ implements InCallStateListener, IncomingCallListener,
+ InCallDetailsListener, CanAddCallListener, CallList.ActiveSubChangeListener,
+ StartInCallCallReceiver.Receiver, ContactInfoCacheCallback, InCallPluginUpdateListener {
+
+ private static final String TAG = ModButtonPresenter.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private Call mCall;
+ private DeepLink mNoteDeepLink;
+ private ContactInfoCache.ContactCacheEntry mPrimaryContactInfo;
+ private StartInCallCallReceiver mCallback;
+
+ @Override
+ public void onReceiveResult(int resultCode, Bundle resultData) {
+ if (DEBUG) Log.i(TAG, "Got InCallPlugin result callback code = " + resultCode);
+
+ switch (resultCode) {
+ case StatusCodes.StartCall.HANDOVER_CONNECTED:
+ if (mCall == null) {
+ return;
+ }
+
+ if (DEBUG) Log.i(TAG, "Disconnecting call: " + mCall);
+ TelecomAdapter.getInstance().disconnectCall(mCall.getId());
+ break;
+ default:
+ Log.i(TAG, "Nothing to do for this InCallPlugin resultcode = " + resultCode);
+ }
+ }
+
+ public ModButtonPresenter() {}
+
+ @Override
+ public void onUiReady(ModButtonUi ui) {
+ super.onUiReady(ui);
+
+ // register for call state changes last
+ final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
+ inCallPresenter.addListener(this);
+ inCallPresenter.addIncomingCallListener(this);
+ inCallPresenter.addDetailsListener(this);
+ inCallPresenter.addCanAddCallListener(this);
+ inCallPresenter.addInCallPluginUpdateListener(this);
+ CallList.getInstance().addActiveSubChangeListener(this);
+
+ // Update the buttons state immediately for the current call
+ onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(),
+ CallList.getInstance());
+ }
+
+ @Override
+ public void onUiUnready(ModButtonUi ui) {
+ super.onUiUnready(ui);
+
+ InCallPresenter.getInstance().removeListener(this);
+ InCallPresenter.getInstance().removeIncomingCallListener(this);
+ InCallPresenter.getInstance().removeDetailsListener(this);
+ InCallPresenter.getInstance().removeCanAddCallListener(this);
+ InCallPresenter.getInstance().removeInCallPluginUpdateListener(this);
+ CallList.getInstance().removeActiveSubChangeListener(this);
+ }
+
+ @Override
+ public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
+
+ if (newState == InCallState.OUTGOING) {
+ mCall = callList.getOutgoingCall();
+ } else if (newState == InCallState.INCALL) {
+ mCall = callList.getActiveOrBackgroundCall();
+ } else if (newState == InCallState.INCOMING) {
+ mCall = callList.getIncomingCall();
+ } else {
+ mCall = null;
+ }
+
+ if (mCall != null && mPrimaryContactInfo == null) {
+ startContactInfoSearch(mCall, newState == InCallState.INCOMING);
+ getPreferredLinks();
+ }
+
+ updateUi(newState, mCall);
+ }
+
+ /**
+ * Starts a query for more contact data for the save primary and secondary calls.
+ */
+ private void startContactInfoSearch(final Call call, boolean isIncoming) {
+ final ContactInfoCache cache = ContactInfoCache.getInstance(getUi().getContext());
+
+ cache.findInfo(call, isIncoming, this);
+ }
+
+ /**
+ * Updates the user interface in response to a change in the details of a call.
+ * Currently handles changes to the call buttons in response to a change in the details for a
+ * call. This is important to ensure changes to the active call are reflected in the available
+ * buttons.
+ *
+ * @param call The active call.
+ * @param details The call details.
+ */
+ @Override
+ public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
+ // Only update if the changes are for the currently active call
+ if (getUi() != null && call != null && call.equals(mCall)) {
+ updateButtonsState(call);
+ }
+ }
+
+ @Override
+ public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
+ onStateChange(oldState, newState, CallList.getInstance());
+ }
+
+ @Override
+ public void onCanAddCallChanged(boolean canAddCall) {
+ if (getUi() != null && mCall != null) {
+ updateButtonsState(mCall);
+ }
+ }
+
+ public List<InCallPluginInfo> getContactInCallPluginInfoList() {
+ List<InCallPluginInfo> inCallPluginInfoList = null;
+ if (mCall != null) {
+ final ContactInfoCache cache = ContactInfoCache.getInstance(getUi().getContext());
+ if (cache != null) {
+ ContactCacheEntry contactInfo = cache.getInfo(mCall.getId());
+ if (contactInfo != null) {
+ inCallPluginInfoList = contactInfo.inCallPluginInfoList;
+ }
+ if (inCallPluginInfoList == null) {
+ cache.refreshPluginInfo(mCall, this);
+ }
+ }
+ }
+ return inCallPluginInfoList;
+ }
+
+ public void switchToVideoCall() {
+ List<InCallPluginInfo> contactInCallPlugins = getContactInCallPluginInfoList();
+ int listSize = (contactInCallPlugins != null) ? contactInCallPlugins.size() : 0;
+ if (listSize == 1) {
+ // If only one InCall Plugin available
+ handoverCallToVoIPPlugin();
+ } else if (listSize > 0){
+ // If multiple sources available
+ getUi().displayVideoCallProviderOptions();
+ }
+ }
+
+ public void handoverCallToVoIPPlugin() {
+ handoverCallToVoIPPlugin(0);
+ }
+
+ public void handoverCallToVoIPPlugin(int contactPluginIndex) {
+ if (getUi() == null || getUi().getContext() == null) {
+ Log.e(TAG, "getUi returned null or can't find context.");
+ return;
+ }
+
+ final Context ctx = getUi().getContext();
+ List<InCallPluginInfo> inCallPluginInfoList = getContactInCallPluginInfoList();
+ if (inCallPluginInfoList != null && inCallPluginInfoList.size() > contactPluginIndex) {
+ InCallPluginInfo info = inCallPluginInfoList.get(contactPluginIndex);
+ final ComponentName component = info.getPluginComponent();
+ final String userId = info.getUserId();
+ final String mimeType = info.getMimeType();
+ if (component != null && !TextUtils.isEmpty(component.flattenToString()) &&
+ !TextUtils.isEmpty(mimeType)) {
+ // Attempt call handover
+ final PendingIntent inviteIntent = info.getPluginInviteIntent();
+ if (!TextUtils.isEmpty(userId)) {
+ AmbientApiClient client =
+ AmbientConnection.CLIENT.get(ctx.getApplicationContext());
+
+ mCallback = new StartInCallCallReceiver(new Handler(Looper.myLooper()));
+ mCallback.setReceiver(ModButtonPresenter.this);
+ StartCallRequest request = new StartCallRequest(userId,
+ OriginCodes.CALL_HANDOVER,
+ StartCallRequest.FLAG_CALL_TRANSFER,
+ mCallback);
+
+ if (DEBUG) Log.i(TAG, "Starting InCallPlugin call for = " + userId);
+ InCallServices.getInstance().startVideoCall(client, component, request);
+ } else {
+ // Attempt invite
+ if (DEBUG) {
+ final ContactInfoCache cache = ContactInfoCache.getInstance(ctx);
+ ContactCacheEntry entry = cache.getInfo(mCall.getId());
+ Uri lookupUri = entry.lookupUri;
+ Log.i(TAG, "Attempting invite for " + lookupUri.toString());
+ }
+
+ String inviteText;
+ if (inviteIntent != null) {
+ // Attempt contact invite
+ inviteText = ctx.getApplicationContext()
+ .getString(R.string.snackbar_incall_plugin_contact_invite,
+ info.getPluginTitle());
+ } else {
+ // Inform user to add contact manually, no invite intent found
+ inviteText = ctx.getApplicationContext()
+ .getString(R.string.snackbar_incall_plugin_no_invite_found,
+ info.getPluginTitle());
+ }
+ getUi().showInviteSnackbar(inviteIntent, inviteText);
+ }
+ }
+ }
+ }
+
+ private void updateUi(InCallState state, Call call) {
+ if (DEBUG) Log.d(this, "Updating call UI for call: ", call);
+
+ final ModButtonUi ui = getUi();
+ if (ui == null) {
+ return;
+ }
+
+ final boolean isProvisioned = isDeviceProvisionedInSettingsDb(ui.getContext());
+ final boolean isEnabled = isProvisioned &&
+ state.isConnectingOrConnected() &&
+ call != null;
+ ui.setEnabled(isEnabled);
+
+ if (call == null) {
+ return;
+ }
+
+ updateButtonsState(call);
+ }
+
+ /**
+ * Updates the buttons applicable for the UI.
+ *
+ * @param call The active call.
+ */
+ private void updateButtonsState(Call call) {
+ Log.v(this, "updateButtonsState");
+ final ModButtonUi ui = getUi();
+ final boolean isProvisioned = isDeviceProvisionedInSettingsDb(ui.getContext());
+
+ List<InCallPluginInfo> contactInCallPlugins = getContactInCallPluginInfoList();
+ final boolean shouldShowInCall = isProvisioned &&
+ contactInCallPlugins != null && !contactInCallPlugins.isEmpty();
+ final boolean showNote = isProvisioned &&
+ DeepLinkIntegrationManager.getInstance().ambientIsAvailable(getUi().getContext()) &&
+ mNoteDeepLink != null;
+
+ ui.showButton(BUTTON_INCALL, shouldShowInCall);
+ if (shouldShowInCall) {
+ ui.modifyChangeToVideoButton();
+ }
+ ui.showButton(BUTTON_TAKE_NOTE, showNote);
+
+ ui.updateButtonStates();
+ }
+
+ private void contactUpdated() {
+ if (DEBUG) Log.i(this, "contactUpdated");
+ if (getUi() != null && mCall != null) {
+ updateButtonsState(mCall);
+ }
+ }
+
+ @Override
+ public void onInCallPluginUpdated() {
+ if (DEBUG) Log.i(this, "onInCallPluginUpdated");
+ contactUpdated();
+ }
+
+ @Override
+ public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
+ if (DEBUG) Log.i(this, "onContactInfoComplete");
+ mPrimaryContactInfo = entry;
+ contactUpdated();
+ }
+
+ @Override
+ public void onImageLoadComplete(String callId, ContactCacheEntry entry) {
+ // Stub
+ }
+
+ public interface ModButtonUi extends Ui {
+ void showButton(int buttonId, boolean show);
+ void enableButton(int buttonId, boolean enable);
+ void setEnabled(boolean on);
+ void modifyChangeToVideoButton();
+ void displayVideoCallProviderOptions();
+ void showInviteSnackbar(PendingIntent inviteIntent, String inviteText);
+ void setDeepLinkNoteIcon(Drawable d);
+
+ /**
+ * 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();
+ }
+
+ public void onActiveSubChanged(int subId) {
+ InCallState state = InCallPresenter.getInstance()
+ .getPotentialStateFromCallList(CallList.getInstance());
+
+ onStateChange(null, state, CallList.getInstance());
+ }
+
+ public void takeNote() {
+ if (mCall != null && mNoteDeepLink != null) {
+ Context ctx = getUi().getContext();
+
+ android.telecom.Call.Details details = mCall.getTelecommCall().getDetails();
+ CallDeepLinkContent content = new CallDeepLinkContent(mNoteDeepLink);
+ content.setName(TextUtils.isEmpty(mPrimaryContactInfo.name) ?
+ ctx.getString(R.string.deeplink_unknown_caller) : mPrimaryContactInfo.name);
+ content.setNumber(mCall.getNumber());
+ content.setUri(DeepLinkIntegrationManager.generateCallUri(mCall.getNumber(),
+ details.getCreateTimeMillis()));
+ DeepLinkIntegrationManager.getInstance().sendContentSentEvent(ctx, mNoteDeepLink,
+ new ComponentName(ctx, CallButtonPresenter.class));
+ ctx.startActivity(content.build());
+
+ }
+ }
+
+ public void getPreferredLinks() {
+ if (mCall != null) {
+ Uri callUri = DeepLinkIntegrationManager.generateCallUri(mCall.getNumber(),
+ mCall.getCreateTimeMillis());
+ DeepLinkIntegrationManager.getInstance().getPreferredLinksFor(mNoteDeepLinkCallback,
+ DeepLinkContentType.CALL, callUri);
+ }
+ }
+
+ private ResultCallback<DeepLink.DeepLinkResultList> mNoteDeepLinkCallback =
+ new ResultCallback<DeepLink.DeepLinkResultList>() {
+ @Override
+ public void onResult(DeepLink.DeepLinkResultList deepLinkResult) {
+ if (getUi() == null) {
+ return;
+ }
+
+ Drawable toDraw = null;
+ if (deepLinkResult != null && deepLinkResult.getResults() != null &&
+ getUi() != null) {
+ List<DeepLink> links = deepLinkResult.getResults();
+ for (DeepLink result : links) {
+ if (result.getApplicationType() == DeepLinkApplicationType.NOTE) {
+ mNoteDeepLink = result;
+ toDraw = result.getDrawableIcon(getUi().getContext()).mutate();
+ break;
+ }
+ }
+ }
+ getUi().setDeepLinkNoteIcon(toDraw);
+ }
+ };
+
+ private boolean isDeviceProvisionedInSettingsDb(Context context) {
+ return (CMSettings.Secure.getInt(context.getContentResolver(),
+ CMSettings.Secure.CM_SETUP_WIZARD_COMPLETED, 0) != 0) &&
+ (Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0);
+ }
+}