14d910f9df60b820d3461995c1d85d0581041b531David Brown/*
24d910f9df60b820d3461995c1d85d0581041b531David Brown * Copyright (C) 2009 The Android Open Source Project
34d910f9df60b820d3461995c1d85d0581041b531David Brown *
44d910f9df60b820d3461995c1d85d0581041b531David Brown * Licensed under the Apache License, Version 2.0 (the "License");
54d910f9df60b820d3461995c1d85d0581041b531David Brown * you may not use this file except in compliance with the License.
64d910f9df60b820d3461995c1d85d0581041b531David Brown * You may obtain a copy of the License at
74d910f9df60b820d3461995c1d85d0581041b531David Brown *
84d910f9df60b820d3461995c1d85d0581041b531David Brown *      http://www.apache.org/licenses/LICENSE-2.0
94d910f9df60b820d3461995c1d85d0581041b531David Brown *
104d910f9df60b820d3461995c1d85d0581041b531David Brown * Unless required by applicable law or agreed to in writing, software
114d910f9df60b820d3461995c1d85d0581041b531David Brown * distributed under the License is distributed on an "AS IS" BASIS,
124d910f9df60b820d3461995c1d85d0581041b531David Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134d910f9df60b820d3461995c1d85d0581041b531David Brown * See the License for the specific language governing permissions and
144d910f9df60b820d3461995c1d85d0581041b531David Brown * limitations under the License.
154d910f9df60b820d3461995c1d85d0581041b531David Brown */
164d910f9df60b820d3461995c1d85d0581041b531David Brown
174d910f9df60b820d3461995c1d85d0581041b531David Brownpackage com.android.phone;
184d910f9df60b820d3461995c1d85d0581041b531David Brown
194d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.content.Context;
20c0005b83ea6ff24b5136fc93d807553922f00458David Brownimport android.graphics.drawable.Drawable;
214d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.os.SystemClock;
224d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.util.AttributeSet;
234d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.util.Log;
244d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.view.LayoutInflater;
25825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brownimport android.view.MotionEvent;
264d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.view.View;
2709337da1891f61ed2a821782f58f1b7d98770682Jim Millerimport android.view.animation.AlphaAnimation;
2809337da1891f61ed2a821782f58f1b7d98770682Jim Millerimport android.view.animation.Animation;
2909337da1891f61ed2a821782f58f1b7d98770682Jim Millerimport android.view.animation.Animation.AnimationListener;
3047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brownimport android.widget.Button;
314d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.widget.FrameLayout;
32c0005b83ea6ff24b5136fc93d807553922f00458David Brownimport android.widget.ImageButton;
33284c588c8d9a1089ba7f22725c8f52a83f267435David Brownimport android.widget.TextView;
3447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brownimport android.widget.ToggleButton;
354d910f9df60b820d3461995c1d85d0581041b531David Brown
36a1bfb5406fa20be8351e272a089c582946e86fc4David Brownimport com.android.internal.telephony.Call;
374d910f9df60b820d3461995c1d85d0581041b531David Brownimport com.android.internal.telephony.Phone;
383948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Millerimport com.android.internal.widget.SlidingTab;
394d910f9df60b820d3461995c1d85d0581041b531David Brown
404d910f9df60b820d3461995c1d85d0581041b531David Brown
414d910f9df60b820d3461995c1d85d0581041b531David Brown/**
424d910f9df60b820d3461995c1d85d0581041b531David Brown * In-call onscreen touch UI elements, used on some platforms.
434d910f9df60b820d3461995c1d85d0581041b531David Brown *
444d910f9df60b820d3461995c1d85d0581041b531David Brown * This widget is a fullscreen overlay, drawn on top of the
454d910f9df60b820d3461995c1d85d0581041b531David Brown * non-touch-sensitive parts of the in-call UI (i.e. the call card).
464d910f9df60b820d3461995c1d85d0581041b531David Brown */
474d910f9df60b820d3461995c1d85d0581041b531David Brownpublic class InCallTouchUi extends FrameLayout
483948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller        implements View.OnClickListener, SlidingTab.OnTriggerListener {
4909337da1891f61ed2a821782f58f1b7d98770682Jim Miller    private static final int IN_CALL_WIDGET_TRANSITION_TIME = 250; // in ms
504d910f9df60b820d3461995c1d85d0581041b531David Brown    private static final String LOG_TAG = "InCallTouchUi";
511e8a3579f8e49fd1f1a078d6be6864a6a8bac306David Brown    private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 2);
524d910f9df60b820d3461995c1d85d0581041b531David Brown
534d910f9df60b820d3461995c1d85d0581041b531David Brown    /**
544d910f9df60b820d3461995c1d85d0581041b531David Brown     * Reference to the InCallScreen activity that owns us.  This may be
554d910f9df60b820d3461995c1d85d0581041b531David Brown     * null if we haven't been initialized yet *or* after the InCallScreen
564d910f9df60b820d3461995c1d85d0581041b531David Brown     * activity has been destroyed.
574d910f9df60b820d3461995c1d85d0581041b531David Brown     */
584d910f9df60b820d3461995c1d85d0581041b531David Brown    private InCallScreen mInCallScreen;
594d910f9df60b820d3461995c1d85d0581041b531David Brown
604d910f9df60b820d3461995c1d85d0581041b531David Brown    // Phone app instance
614d910f9df60b820d3461995c1d85d0581041b531David Brown    private PhoneApp mApplication;
624d910f9df60b820d3461995c1d85d0581041b531David Brown
632b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    // UI containers / elements
643948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller    private SlidingTab mIncomingCallWidget;  // UI used for an incoming call
654d910f9df60b820d3461995c1d85d0581041b531David Brown    private View mInCallControls;  // UI elements while on a regular call
6647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    //
67284c588c8d9a1089ba7f22725c8f52a83f267435David Brown    private Button mAddButton;
68284c588c8d9a1089ba7f22725c8f52a83f267435David Brown    private Button mMergeButton;
69284c588c8d9a1089ba7f22725c8f52a83f267435David Brown    private Button mEndButton;
7047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private Button mDialpadButton;
7147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private ToggleButton mBluetoothButton;
7247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private ToggleButton mMuteButton;
7347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private ToggleButton mSpeakerButton;
74c0005b83ea6ff24b5136fc93d807553922f00458David Brown    //
75284c588c8d9a1089ba7f22725c8f52a83f267435David Brown    private View mHoldButtonContainer;
76284c588c8d9a1089ba7f22725c8f52a83f267435David Brown    private ImageButton mHoldButton;
77284c588c8d9a1089ba7f22725c8f52a83f267435David Brown    private TextView mHoldButtonLabel;
78284c588c8d9a1089ba7f22725c8f52a83f267435David Brown    private View mSwapButtonContainer;
79284c588c8d9a1089ba7f22725c8f52a83f267435David Brown    private ImageButton mSwapButton;
8039a84144262f12df44861d746788b9de92259e4fAbhishek Pillai    private TextView mSwapButtonLabel;
8139a84144262f12df44861d746788b9de92259e4fAbhishek Pillai    private View mCdmaMergeButtonContainer;
8239a84144262f12df44861d746788b9de92259e4fAbhishek Pillai    private ImageButton mCdmaMergeButton;
83c0005b83ea6ff24b5136fc93d807553922f00458David Brown    //
84c0005b83ea6ff24b5136fc93d807553922f00458David Brown    private Drawable mHoldIcon;
85c0005b83ea6ff24b5136fc93d807553922f00458David Brown    private Drawable mUnholdIcon;
86c0005b83ea6ff24b5136fc93d807553922f00458David Brown    private Drawable mShowDialpadIcon;
87c0005b83ea6ff24b5136fc93d807553922f00458David Brown    private Drawable mHideDialpadIcon;
884d910f9df60b820d3461995c1d85d0581041b531David Brown
89a1bfb5406fa20be8351e272a089c582946e86fc4David Brown    // Time of the most recent "answer" or "reject" action (see updateState())
90a1bfb5406fa20be8351e272a089c582946e86fc4David Brown    private long mLastIncomingCallActionTime;  // in SystemClock.uptimeMillis() time base
914d910f9df60b820d3461995c1d85d0581041b531David Brown
924d910f9df60b820d3461995c1d85d0581041b531David Brown    // Overall enabledness of the "touch UI" features
934d910f9df60b820d3461995c1d85d0581041b531David Brown    private boolean mAllowIncomingCallTouchUi;
944d910f9df60b820d3461995c1d85d0581041b531David Brown    private boolean mAllowInCallTouchUi;
954d910f9df60b820d3461995c1d85d0581041b531David Brown
964d910f9df60b820d3461995c1d85d0581041b531David Brown    public InCallTouchUi(Context context, AttributeSet attrs) {
974d910f9df60b820d3461995c1d85d0581041b531David Brown        super(context, attrs);
984d910f9df60b820d3461995c1d85d0581041b531David Brown
994d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("InCallTouchUi constructor...");
1004d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("- this = " + this);
1014d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("- context " + context + ", attrs " + attrs);
1024d910f9df60b820d3461995c1d85d0581041b531David Brown
1034d910f9df60b820d3461995c1d85d0581041b531David Brown        // Inflate our contents, and add it (to ourself) as a child.
1044d910f9df60b820d3461995c1d85d0581041b531David Brown        LayoutInflater inflater = LayoutInflater.from(context);
1054d910f9df60b820d3461995c1d85d0581041b531David Brown        inflater.inflate(
1064d910f9df60b820d3461995c1d85d0581041b531David Brown                R.layout.incall_touch_ui,  // resource
1074d910f9df60b820d3461995c1d85d0581041b531David Brown                this,                      // root
1084d910f9df60b820d3461995c1d85d0581041b531David Brown                true);
1094d910f9df60b820d3461995c1d85d0581041b531David Brown
1104d910f9df60b820d3461995c1d85d0581041b531David Brown        mApplication = PhoneApp.getInstance();
1114d910f9df60b820d3461995c1d85d0581041b531David Brown
1124d910f9df60b820d3461995c1d85d0581041b531David Brown        // The various touch UI features are enabled on a per-product
1134d910f9df60b820d3461995c1d85d0581041b531David Brown        // basis.  (These flags in config.xml may be overridden by
1144d910f9df60b820d3461995c1d85d0581041b531David Brown        // product-specific overlay files.)
1154d910f9df60b820d3461995c1d85d0581041b531David Brown
1164d910f9df60b820d3461995c1d85d0581041b531David Brown        mAllowIncomingCallTouchUi = getResources().getBoolean(R.bool.allow_incoming_call_touch_ui);
1174d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("- incoming call touch UI: "
1184d910f9df60b820d3461995c1d85d0581041b531David Brown                     + (mAllowIncomingCallTouchUi ? "ENABLED" : "DISABLED"));
1194d910f9df60b820d3461995c1d85d0581041b531David Brown        mAllowInCallTouchUi = getResources().getBoolean(R.bool.allow_in_call_touch_ui);
1204d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("- regular in-call touch UI: "
1214d910f9df60b820d3461995c1d85d0581041b531David Brown                     + (mAllowInCallTouchUi ? "ENABLED" : "DISABLED"));
1224d910f9df60b820d3461995c1d85d0581041b531David Brown    }
1234d910f9df60b820d3461995c1d85d0581041b531David Brown
1244d910f9df60b820d3461995c1d85d0581041b531David Brown    void setInCallScreenInstance(InCallScreen inCallScreen) {
1254d910f9df60b820d3461995c1d85d0581041b531David Brown        mInCallScreen = inCallScreen;
1264d910f9df60b820d3461995c1d85d0581041b531David Brown    }
1274d910f9df60b820d3461995c1d85d0581041b531David Brown
1284d910f9df60b820d3461995c1d85d0581041b531David Brown    @Override
1294d910f9df60b820d3461995c1d85d0581041b531David Brown    protected void onFinishInflate() {
1304d910f9df60b820d3461995c1d85d0581041b531David Brown        super.onFinishInflate();
1314d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("InCallTouchUi onFinishInflate(this = " + this + ")...");
1324d910f9df60b820d3461995c1d85d0581041b531David Brown
1334d910f9df60b820d3461995c1d85d0581041b531David Brown        // Look up the various UI elements.
13420d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown
13520d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown        // "Dial-to-answer" widget for incoming calls.
1363948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller        mIncomingCallWidget = (SlidingTab) findViewById(R.id.incomingCallWidget);
1373948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller        mIncomingCallWidget.setLeftTabResources(
1383948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                R.drawable.ic_jog_dial_answer,
1393948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                com.android.internal.R.drawable.jog_tab_target_green,
1403948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                com.android.internal.R.drawable.jog_tab_bar_left_answer,
1413948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                com.android.internal.R.drawable.jog_tab_left_answer
1423948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                );
1433948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller        mIncomingCallWidget.setRightTabResources(
1443948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                R.drawable.ic_jog_dial_decline,
1453948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                com.android.internal.R.drawable.jog_tab_target_red,
1463948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                com.android.internal.R.drawable.jog_tab_bar_right_decline,
1473948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                com.android.internal.R.drawable.jog_tab_right_decline
1483948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                );
1493948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller
150b7ea5aff1b9f1dc965a9b9f6882b8ea951a3d8aeJim Miller        // For now, we only need to show two states: answer and decline.
1513948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller        mIncomingCallWidget.setLeftHintText(R.string.slide_to_answer_hint);
1523948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller        mIncomingCallWidget.setRightHintText(R.string.slide_to_decline_hint);
1533948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller
1543948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller        mIncomingCallWidget.setOnTriggerListener(this);
15520d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown
15620d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown        // Container for the UI elements shown while on a regular call.
1574d910f9df60b820d3461995c1d85d0581041b531David Brown        mInCallControls = findViewById(R.id.inCallControls);
1584d910f9df60b820d3461995c1d85d0581041b531David Brown
1592b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        // Regular (single-tap) buttons, where we listen for click events:
16047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // Main cluster of buttons:
161284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mAddButton = (Button) mInCallControls.findViewById(R.id.addButton);
162284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mAddButton.setOnClickListener(this);
163284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mMergeButton = (Button) mInCallControls.findViewById(R.id.mergeButton);
164284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mMergeButton.setOnClickListener(this);
165284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mEndButton = (Button) mInCallControls.findViewById(R.id.endButton);
166284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mEndButton.setOnClickListener(this);
16747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mDialpadButton = (Button) mInCallControls.findViewById(R.id.dialpadButton);
16847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mDialpadButton.setOnClickListener(this);
16947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mBluetoothButton = (ToggleButton) mInCallControls.findViewById(R.id.bluetoothButton);
17047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mBluetoothButton.setOnClickListener(this);
17147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMuteButton = (ToggleButton) mInCallControls.findViewById(R.id.muteButton);
17247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMuteButton.setOnClickListener(this);
17347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSpeakerButton = (ToggleButton) mInCallControls.findViewById(R.id.speakerButton);
17447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSpeakerButton.setOnClickListener(this);
175c0005b83ea6ff24b5136fc93d807553922f00458David Brown
17647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // Upper corner buttons:
177284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mHoldButtonContainer = mInCallControls.findViewById(R.id.holdButtonContainer);
178284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mHoldButton = (ImageButton) mInCallControls.findViewById(R.id.holdButton);
179284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mHoldButton.setOnClickListener(this);
180284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mHoldButtonLabel = (TextView) mInCallControls.findViewById(R.id.holdButtonLabel);
181c0005b83ea6ff24b5136fc93d807553922f00458David Brown        //
182284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mSwapButtonContainer = mInCallControls.findViewById(R.id.swapButtonContainer);
183284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mSwapButton = (ImageButton) mInCallControls.findViewById(R.id.swapButton);
184284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mSwapButton.setOnClickListener(this);
18539a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        mSwapButtonLabel = (TextView) mInCallControls.findViewById(R.id.swapButtonLabel);
18639a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        if (PhoneApp.getInstance().phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
18739a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            // In CDMA we use a generalized text - "Manage call", as behavior on selecting
18839a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            // this option depends entirely on what the current call state is.
18939a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            mSwapButtonLabel.setText(R.string.onscreenManageCallsText);
19039a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        } else {
19139a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            mSwapButtonLabel.setText(R.string.onscreenSwapCallsText);
19239a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        }
19339a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        //
19439a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        mCdmaMergeButtonContainer = mInCallControls.findViewById(R.id.cdmaMergeButtonContainer);
19539a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        mCdmaMergeButton = (ImageButton) mInCallControls.findViewById(R.id.cdmaMergeButton);
19639a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        mCdmaMergeButton.setOnClickListener(this);
197c0005b83ea6ff24b5136fc93d807553922f00458David Brown
198825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // Add a custom OnTouchListener to manually shrink the "hit
199825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // target" of some buttons.
200825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // (We do this for a few specific buttons which are vulnerable to
201825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // "false touches" because either (1) they're near the edge of the
202825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // screen and might be unintentionally touched while holding the
203825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // device in your hand, or (2) they're in the upper corners and might
204825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // be touched by the user's ear before the prox sensor has a chance to
205825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // kick in.)
206825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        View.OnTouchListener smallerHitTargetTouchListener = new SmallerHitTargetTouchListener();
207825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        mAddButton.setOnTouchListener(smallerHitTargetTouchListener);
208825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        mMergeButton.setOnTouchListener(smallerHitTargetTouchListener);
209825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        mDialpadButton.setOnTouchListener(smallerHitTargetTouchListener);
210825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        mBluetoothButton.setOnTouchListener(smallerHitTargetTouchListener);
211825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        mSpeakerButton.setOnTouchListener(smallerHitTargetTouchListener);
212825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        mHoldButton.setOnTouchListener(smallerHitTargetTouchListener);
213825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        mSwapButton.setOnTouchListener(smallerHitTargetTouchListener);
214825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        mCdmaMergeButton.setOnTouchListener(smallerHitTargetTouchListener);
215825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        mSpeakerButton.setOnTouchListener(smallerHitTargetTouchListener);
216825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
217284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        // Icons we need to change dynamically.  (Most other icons are specified
218284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        // directly in incall_touch_ui.xml.)
219284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mHoldIcon = getResources().getDrawable(R.drawable.ic_in_call_touch_round_hold);
220284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mUnholdIcon = getResources().getDrawable(R.drawable.ic_in_call_touch_round_unhold);
221c0005b83ea6ff24b5136fc93d807553922f00458David Brown        mShowDialpadIcon = getResources().getDrawable(R.drawable.ic_in_call_touch_dialpad);
222c0005b83ea6ff24b5136fc93d807553922f00458David Brown        mHideDialpadIcon = getResources().getDrawable(R.drawable.ic_in_call_touch_dialpad_close);
2234d910f9df60b820d3461995c1d85d0581041b531David Brown    }
2244d910f9df60b820d3461995c1d85d0581041b531David Brown
2254d910f9df60b820d3461995c1d85d0581041b531David Brown    /**
2264d910f9df60b820d3461995c1d85d0581041b531David Brown     * Updates the visibility and/or state of our UI elements, based on
2274d910f9df60b820d3461995c1d85d0581041b531David Brown     * the current state of the phone.
2284d910f9df60b820d3461995c1d85d0581041b531David Brown     */
2294d910f9df60b820d3461995c1d85d0581041b531David Brown    void updateState(Phone phone) {
2304d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("updateState(" + phone + ")...");
2314d910f9df60b820d3461995c1d85d0581041b531David Brown
2321f3361d1fd6f0ee60362cecf686b7cf997ed485bDavid Brown        if (mInCallScreen == null) {
2331f3361d1fd6f0ee60362cecf686b7cf997ed485bDavid Brown            log("- updateState: mInCallScreen has been destroyed; bailing out...");
2341f3361d1fd6f0ee60362cecf686b7cf997ed485bDavid Brown            return;
2351f3361d1fd6f0ee60362cecf686b7cf997ed485bDavid Brown        }
2361f3361d1fd6f0ee60362cecf686b7cf997ed485bDavid Brown
2374d910f9df60b820d3461995c1d85d0581041b531David Brown        Phone.State state = phone.getState();  // IDLE, RINGING, or OFFHOOK
238a1bfb5406fa20be8351e272a089c582946e86fc4David Brown        if (DBG) log("- updateState: phone state is " + state);
2394d910f9df60b820d3461995c1d85d0581041b531David Brown
2404d910f9df60b820d3461995c1d85d0581041b531David Brown        boolean showIncomingCallControls = false;
2414d910f9df60b820d3461995c1d85d0581041b531David Brown        boolean showInCallControls = false;
2424d910f9df60b820d3461995c1d85d0581041b531David Brown
2434d910f9df60b820d3461995c1d85d0581041b531David Brown        if (state == Phone.State.RINGING) {
2444d910f9df60b820d3461995c1d85d0581041b531David Brown            // A phone call is ringing *or* call waiting.
2454d910f9df60b820d3461995c1d85d0581041b531David Brown            if (mAllowIncomingCallTouchUi) {
246a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                // Watch out: even if the phone state is RINGING, it's
247a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                // possible for the ringing call to be in the DISCONNECTING
248a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                // state.  (This typically happens immediately after the user
249a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                // rejects an incoming call, and in that case we *don't* show
250a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                // the incoming call controls.)
251a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                final Call ringingCall = phone.getRingingCall();
252a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                if (ringingCall.getState().isAlive()) {
253a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                    if (DBG) log("- updateState: RINGING!  Showing incoming call controls...");
254a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                    showIncomingCallControls = true;
255a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                }
256a1bfb5406fa20be8351e272a089c582946e86fc4David Brown
257a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                // Ugly hack to cover up slow response from the radio:
258a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                // if we attempted to answer or reject an incoming call
259a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                // within the last 500 msec, *don't* show the incoming call
260a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                // UI even if the phone is still in the RINGING state.
261a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                long now = SystemClock.uptimeMillis();
262a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                if (now < mLastIncomingCallActionTime + 500) {
263a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                    log("updateState: Too soon after last action; not drawing!");
264a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                    showIncomingCallControls = false;
265a1bfb5406fa20be8351e272a089c582946e86fc4David Brown                }
2664d910f9df60b820d3461995c1d85d0581041b531David Brown
2674d910f9df60b820d3461995c1d85d0581041b531David Brown                // TODO: UI design issue: if the device is NOT currently
2684d910f9df60b820d3461995c1d85d0581041b531David Brown                // locked, we probably don't need to make the user
2694d910f9df60b820d3461995c1d85d0581041b531David Brown                // double-tap the "incoming call" buttons.  (The device
2704d910f9df60b820d3461995c1d85d0581041b531David Brown                // presumably isn't in a pocket or purse, so we don't need
2714d910f9df60b820d3461995c1d85d0581041b531David Brown                // to worry about false touches while it's ringing.)
2724d910f9df60b820d3461995c1d85d0581041b531David Brown                // But OTOH having "inconsistent" buttons might just make
2734d910f9df60b820d3461995c1d85d0581041b531David Brown                // it *more* confusing.
2744d910f9df60b820d3461995c1d85d0581041b531David Brown            }
2754d910f9df60b820d3461995c1d85d0581041b531David Brown        } else {
2764d910f9df60b820d3461995c1d85d0581041b531David Brown            if (mAllowInCallTouchUi) {
2772b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // Ok, the in-call touch UI is available on this platform,
2782b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // so make it visible (with some exceptions):
2792b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                if (mInCallScreen.okToShowInCallTouchUi()) {
2802b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                    showInCallControls = true;
2812b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                } else {
2822b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                    if (DBG) log("- updateState: NOT OK to show touch UI; disabling...");
2832b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                }
2844d910f9df60b820d3461995c1d85d0581041b531David Brown            }
2854d910f9df60b820d3461995c1d85d0581041b531David Brown        }
2864d910f9df60b820d3461995c1d85d0581041b531David Brown
2872b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        if (showInCallControls) {
2882b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            updateInCallControls(phone);
2892b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        }
2902b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
2914d910f9df60b820d3461995c1d85d0581041b531David Brown        if (showIncomingCallControls && showInCallControls) {
2924d910f9df60b820d3461995c1d85d0581041b531David Brown            throw new IllegalStateException(
2934d910f9df60b820d3461995c1d85d0581041b531David Brown                "'Incoming' and 'in-call' touch controls visible at the same time!");
2944d910f9df60b820d3461995c1d85d0581041b531David Brown        }
2953964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller
2963964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller        if (showIncomingCallControls) {
2973964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller            showIncomingCallWidget();
2983964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller        } else {
2993964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller            hideIncomingCallWidget();
3003964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller        }
3013964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller
3024d910f9df60b820d3461995c1d85d0581041b531David Brown        mInCallControls.setVisibility(showInCallControls ? View.VISIBLE : View.GONE);
3034d910f9df60b820d3461995c1d85d0581041b531David Brown
3044d910f9df60b820d3461995c1d85d0581041b531David Brown        // TODO: As an optimization, also consider setting the visibility
30547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // of the overall InCallTouchUi widget to GONE if *nothing at all*
3064d910f9df60b820d3461995c1d85d0581041b531David Brown        // is visible right now.
3074d910f9df60b820d3461995c1d85d0581041b531David Brown    }
3084d910f9df60b820d3461995c1d85d0581041b531David Brown
3094d910f9df60b820d3461995c1d85d0581041b531David Brown    // View.OnClickListener implementation
3104d910f9df60b820d3461995c1d85d0581041b531David Brown    public void onClick(View view) {
3114d910f9df60b820d3461995c1d85d0581041b531David Brown        int id = view.getId();
3124d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("onClick(View " + view + ", id " + id + ")...");
3134d910f9df60b820d3461995c1d85d0581041b531David Brown
3142b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        switch (id) {
315284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            case R.id.addButton:
316284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            case R.id.mergeButton:
317284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            case R.id.endButton:
3182b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.dialpadButton:
3192b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.bluetoothButton:
3202b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.muteButton:
3212b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.speakerButton:
322284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            case R.id.holdButton:
323284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            case R.id.swapButton:
32439a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            case R.id.cdmaMergeButton:
3252b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // Clicks on the regular onscreen buttons get forwarded
3262b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // straight to the InCallScreen.
3272b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                mInCallScreen.handleOnscreenButtonClick(id);
3282b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                break;
3292b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
3302b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            default:
3311e8a3579f8e49fd1f1a078d6be6864a6a8bac306David Brown                Log.w(LOG_TAG, "onClick: unexpected click: View " + view + ", id " + id);
3321e8a3579f8e49fd1f1a078d6be6864a6a8bac306David Brown                break;
3332b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        }
3344d910f9df60b820d3461995c1d85d0581041b531David Brown    }
3354d910f9df60b820d3461995c1d85d0581041b531David Brown
3362b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    /**
33747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown     * Updates the enabledness and "checked" state of the buttons on the
3382b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown     * "inCallControls" panel, based on the current telephony state.
3392b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown     */
3402b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    void updateInCallControls(Phone phone) {
34139a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        int phoneType = phone.getPhoneType();
34247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // Note we do NOT need to worry here about cases where the entire
34347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // in-call touch UI is disabled, like during an OTA call or if the
34447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // dtmf dialpad is up.  (That's handled by updateState(), which
34547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // calls InCallScreen.okToShowInCallTouchUi().)
34647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        //
34747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // If we get here, it *is* OK to show the in-call touch UI, so we
34847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // now need to update the enabledness and/or "checked" state of
34947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // each individual button.
35047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        //
35147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
35247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // The InCallControlState object tells us the enabledness and/or
35347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // state of the various onscreen buttons:
35447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        InCallControlState inCallControlState = mInCallScreen.getUpdatedInCallControlState();
35547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
356284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        // "Add" or "Merge":
357284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        // These two buttons occupy the same space onscreen, so only
358284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        // one of them should be available at a given moment.
359284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        if (inCallControlState.canAddCall) {
360284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            mAddButton.setVisibility(View.VISIBLE);
361284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            mAddButton.setEnabled(true);
362284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            mMergeButton.setVisibility(View.GONE);
363284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        } else if (inCallControlState.canMerge) {
36439a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            if (phoneType == Phone.PHONE_TYPE_CDMA) {
36539a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // In CDMA "Add" option is always given to the user and the
36639a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // "Merge" option is provided as a button on the top left corner of the screen,
36739a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // we always set the mMergeButton to GONE
36839a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                mMergeButton.setVisibility(View.GONE);
36939a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            } else if (phoneType == Phone.PHONE_TYPE_GSM) {
37039a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                mMergeButton.setVisibility(View.VISIBLE);
37139a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                mMergeButton.setEnabled(true);
37239a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                mAddButton.setVisibility(View.GONE);
37339a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            } else {
37439a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                throw new IllegalStateException("Unexpected phone type: " + phoneType);
37539a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            }
376c0005b83ea6ff24b5136fc93d807553922f00458David Brown        } else {
377284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // Neither "Add" nor "Merge" is available.  (This happens in
378284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // some transient states, like while dialing an outgoing call,
379284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // and in other rare cases like if you have both lines in use
380284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // *and* there are already 5 people on the conference call.)
381284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // Since the common case here is "while dialing", we show the
382284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // "Add" button in a disabled state so that there won't be any
383284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // jarring change in the UI when the call finally connects.
384284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            mAddButton.setVisibility(View.VISIBLE);
385284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            mAddButton.setEnabled(false);
386284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            mMergeButton.setVisibility(View.GONE);
387c0005b83ea6ff24b5136fc93d807553922f00458David Brown        }
388284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        if (inCallControlState.canAddCall && inCallControlState.canMerge) {
38939a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            if (phoneType == Phone.PHONE_TYPE_GSM) {
39039a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // Uh oh, the InCallControlState thinks that "Add" *and* "Merge"
39139a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // should both be available right now.  This *should* never
39239a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // happen with GSM, but if it's possible on any
39339a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // future devices we may need to re-layout Add and Merge so
39439a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // they can both be visible at the same time...
39539a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                Log.w(LOG_TAG, "updateInCallControls: Add *and* Merge enabled," +
39639a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                        " but can't show both!");
39739a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            } else if (phoneType == Phone.PHONE_TYPE_CDMA) {
39839a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // In CDMA "Add" option is always given to the user and the hence
39939a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // in this case both "Add" and "Merge" options would be available to user
40039a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                if (DBG) log("updateInCallControls: CDMA: Add and Merge both enabled");
40139a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            } else {
40239a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                throw new IllegalStateException("Unexpected phone type: " + phoneType);
40339a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            }
404c0005b83ea6ff24b5136fc93d807553922f00458David Brown        }
40547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
40647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "End call": this button has no state and it's always enabled.
407284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mEndButton.setEnabled(true);
40847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
40947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Dialpad": Enabled only when it's OK to use the dialpad in the
41047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // first place.
41147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mDialpadButton.setEnabled(inCallControlState.dialpadEnabled);
412c0005b83ea6ff24b5136fc93d807553922f00458David Brown        //
4135aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown        if (inCallControlState.dialpadVisible) {
4145aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown            // Show the "hide dialpad" state.
4155aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown            mDialpadButton.setText(R.string.onscreenHideDialpadText);
4165aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown            mDialpadButton.setCompoundDrawablesWithIntrinsicBounds(
4175aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown                null, mHideDialpadIcon, null, null);
4185aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown        } else {
4195aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown            // Show the "show dialpad" state.
4205aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown            mDialpadButton.setText(R.string.onscreenShowDialpadText);
4215aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown            mDialpadButton.setCompoundDrawablesWithIntrinsicBounds(
4225aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown                    null, mShowDialpadIcon, null, null);
4235aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown        }
42447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
42547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Bluetooth"
42647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mBluetoothButton.setEnabled(inCallControlState.bluetoothEnabled);
42747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mBluetoothButton.setChecked(inCallControlState.bluetoothIndicatorOn);
42847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
42947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Mute"
43047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMuteButton.setEnabled(inCallControlState.canMute);
43147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMuteButton.setChecked(inCallControlState.muteIndicatorOn);
43247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
43347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Speaker"
43447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSpeakerButton.setEnabled(inCallControlState.speakerEnabled);
43547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSpeakerButton.setChecked(inCallControlState.speakerOn);
43647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
437284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        // "Hold"
438284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        // (Note "Hold" and "Swap" are never both available at
43947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // the same time.  That's why it's OK for them to both be in the
44047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // same position onscreen.)
44147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // This button is totally hidden (rather than just disabled)
44247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // when the operation isn't available.
443284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mHoldButtonContainer.setVisibility(
444284c588c8d9a1089ba7f22725c8f52a83f267435David Brown                inCallControlState.canHold ? View.VISIBLE : View.GONE);
445284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        if (inCallControlState.canHold) {
446284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // The Hold button icon and label (either "Hold" or "Unhold")
447284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // depend on the current Hold state.
448284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            if (inCallControlState.onHold) {
449284c588c8d9a1089ba7f22725c8f52a83f267435David Brown                mHoldButton.setImageDrawable(mUnholdIcon);
450284c588c8d9a1089ba7f22725c8f52a83f267435David Brown                mHoldButtonLabel.setText(R.string.onscreenUnholdText);
451284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            } else {
452284c588c8d9a1089ba7f22725c8f52a83f267435David Brown                mHoldButton.setImageDrawable(mHoldIcon);
453284c588c8d9a1089ba7f22725c8f52a83f267435David Brown                mHoldButtonLabel.setText(R.string.onscreenHoldText);
454284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            }
455284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        }
45647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
457284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        // "Swap"
45847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // This button is totally hidden (rather than just disabled)
45947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // when the operation isn't available.
460284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        mSwapButtonContainer.setVisibility(
461284c588c8d9a1089ba7f22725c8f52a83f267435David Brown                inCallControlState.canSwap ? View.VISIBLE : View.GONE);
462284c588c8d9a1089ba7f22725c8f52a83f267435David Brown
46339a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
46439a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            // "Merge"
46539a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            // This button is totally hidden (rather than just disabled)
46639a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            // when the operation isn't available.
46739a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            mCdmaMergeButtonContainer.setVisibility(
46839a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                    inCallControlState.canMerge ? View.VISIBLE : View.GONE);
46939a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        }
47039a84144262f12df44861d746788b9de92259e4fAbhishek Pillai
471284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        if (inCallControlState.canSwap && inCallControlState.canHold) {
472284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // Uh oh, the InCallControlState thinks that Swap *and* Hold
473284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // should both be available.  This *should* never happen with
474284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // either GSM or CDMA, but if it's possible on any future
475284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // devices we may need to re-layout Hold and Swap so they can
476284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            // both be visible at the same time...
477284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            Log.w(LOG_TAG, "updateInCallControls: Hold *and* Swap enabled, but can't show both!");
478284c588c8d9a1089ba7f22725c8f52a83f267435David Brown        }
47947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
48039a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        if (phoneType == Phone.PHONE_TYPE_CDMA) {
48139a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            if (inCallControlState.canSwap && inCallControlState.canMerge) {
48239a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // Uh oh, the InCallControlState thinks that Swap *and* Merge
48339a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // should both be available.  This *should* never happen with
48439a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // CDMA, but if it's possible on any future
48539a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // devices we may need to re-layout Merge and Swap so they can
48639a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                // both be visible at the same time...
48739a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                Log.w(LOG_TAG, "updateInCallControls: Merge *and* Swap" +
48839a84144262f12df44861d746788b9de92259e4fAbhishek Pillai                        "enabled, but can't show both!");
48939a84144262f12df44861d746788b9de92259e4fAbhishek Pillai            }
49039a84144262f12df44861d746788b9de92259e4fAbhishek Pillai        }
49139a84144262f12df44861d746788b9de92259e4fAbhishek Pillai
4925aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown        // One final special case: if the dialpad is visible, that trumps
4935aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown        // *any* of the upper corner buttons:
4945aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown        if (inCallControlState.dialpadVisible) {
495284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            mHoldButtonContainer.setVisibility(View.GONE);
496284c588c8d9a1089ba7f22725c8f52a83f267435David Brown            mSwapButtonContainer.setVisibility(View.GONE);
497af7374137755158062fd65dffe762566ab154d49David Brown            mCdmaMergeButtonContainer.setVisibility(View.GONE);
4985aadc2633b13d6676598dc0954be25c108d05fcdDavid Brown        }
4992b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    }
5002b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
5012b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    //
5022b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    // InCallScreen API
5032b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    //
5042b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
5052b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    /**
5062b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown     * @return true if the onscreen touch UI is enabled (for regular
5072b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown     * "ongoing call" states) on the current device.
5082b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown     */
5096d5544932495d81ff7ce53f4678c4beb25acc1b0David Brown    /* package */ boolean isTouchUiEnabled() {
5102b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        return mAllowInCallTouchUi;
5112b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    }
5122b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
5136d50f0fd38eb801c2d5304792fcf72e769cb8d72David Brown    /**
5146d50f0fd38eb801c2d5304792fcf72e769cb8d72David Brown     * @return true if the onscreen touch UI is enabled for
5156d50f0fd38eb801c2d5304792fcf72e769cb8d72David Brown     * the "incoming call" state on the current device.
5166d50f0fd38eb801c2d5304792fcf72e769cb8d72David Brown     */
5176d50f0fd38eb801c2d5304792fcf72e769cb8d72David Brown    /* package */ boolean isIncomingCallTouchUiEnabled() {
5186d50f0fd38eb801c2d5304792fcf72e769cb8d72David Brown        return mAllowIncomingCallTouchUi;
5196d50f0fd38eb801c2d5304792fcf72e769cb8d72David Brown    }
5206d50f0fd38eb801c2d5304792fcf72e769cb8d72David Brown
521a1bfb5406fa20be8351e272a089c582946e86fc4David Brown    //
5223948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller    // SlidingTab.OnTriggerListener implementation
523a1bfb5406fa20be8351e272a089c582946e86fc4David Brown    //
524a1bfb5406fa20be8351e272a089c582946e86fc4David Brown
52520d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown    /**
52620d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown     * Handles "Answer" and "Reject" actions for an incoming call.
5273948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller     * We get this callback from the SlidingTab
528a1bfb5406fa20be8351e272a089c582946e86fc4David Brown     * when the user triggers an action.
529a1bfb5406fa20be8351e272a089c582946e86fc4David Brown     *
530a1bfb5406fa20be8351e272a089c582946e86fc4David Brown     * To answer or reject the incoming call, we call
531a1bfb5406fa20be8351e272a089c582946e86fc4David Brown     * InCallScreen.handleOnscreenButtonClick() and pass one of the
532a1bfb5406fa20be8351e272a089c582946e86fc4David Brown     * special "virtual button" IDs:
533a1bfb5406fa20be8351e272a089c582946e86fc4David Brown     *   - R.id.answerButton to answer the call
534a1bfb5406fa20be8351e272a089c582946e86fc4David Brown     * or
535a1bfb5406fa20be8351e272a089c582946e86fc4David Brown     *   - R.id.rejectButton to reject the call.
536a1bfb5406fa20be8351e272a089c582946e86fc4David Brown     */
5373948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller    public void onTrigger(View v, int whichHandle) {
5381e8a3579f8e49fd1f1a078d6be6864a6a8bac306David Brown        log("onDialTrigger(whichHandle = " + whichHandle + ")...");
53920d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown
54020d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown        switch (whichHandle) {
5413948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller            case SlidingTab.OnTriggerListener.LEFT_HANDLE:
54220d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                if (DBG) log("LEFT_HANDLE: answer!");
54320d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown
54409337da1891f61ed2a821782f58f1b7d98770682Jim Miller                hideIncomingCallWidget();
54509337da1891f61ed2a821782f58f1b7d98770682Jim Miller
54620d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                // ...and also prevent it from reappearing right away.
54720d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                // (This covers up a slow response from the radio; see updateState().)
54820d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                mLastIncomingCallActionTime = SystemClock.uptimeMillis();
54920d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown
55020d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                // Do the appropriate action.
55120d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                if (mInCallScreen != null) {
55220d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                    // Send this to the InCallScreen as a virtual "button click" event:
55320d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                    mInCallScreen.handleOnscreenButtonClick(R.id.answerButton);
55420d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                } else {
55520d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                    Log.e(LOG_TAG, "answer trigger: mInCallScreen is null");
55620d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                }
55720d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                break;
558a1bfb5406fa20be8351e272a089c582946e86fc4David Brown
5593948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller            case SlidingTab.OnTriggerListener.RIGHT_HANDLE:
56020d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                if (DBG) log("RIGHT_HANDLE: reject!");
561a1bfb5406fa20be8351e272a089c582946e86fc4David Brown
56209337da1891f61ed2a821782f58f1b7d98770682Jim Miller                hideIncomingCallWidget();
56309337da1891f61ed2a821782f58f1b7d98770682Jim Miller
56420d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                // ...and also prevent it from reappearing right away.
56520d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                // (This covers up a slow response from the radio; see updateState().)
56620d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                mLastIncomingCallActionTime = SystemClock.uptimeMillis();
567a1bfb5406fa20be8351e272a089c582946e86fc4David Brown
56820d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                // Do the appropriate action.
56920d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                if (mInCallScreen != null) {
57020d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                    // Send this to the InCallScreen as a virtual "button click" event:
57120d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                    mInCallScreen.handleOnscreenButtonClick(R.id.rejectButton);
57220d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                } else {
57320d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                    Log.e(LOG_TAG, "reject trigger: mInCallScreen is null");
57420d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                }
57520d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                break;
576a1bfb5406fa20be8351e272a089c582946e86fc4David Brown
57720d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown            default:
57820d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                Log.e(LOG_TAG, "onDialTrigger: unexpected whichHandle value: " + whichHandle);
57920d7df041ea1a115de6e5f0b6e236ae6e17b2fccDavid Brown                break;
580a1bfb5406fa20be8351e272a089c582946e86fc4David Brown        }
5819688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown
5829688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown        // Regardless of what action the user did, be sure to clear out
5839688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown        // the hint text we were displaying while the user was dragging.
5843948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller        mInCallScreen.updateSlidingTabHint(0, 0);
5859688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown    }
5869688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown
5879688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown    /**
58809337da1891f61ed2a821782f58f1b7d98770682Jim Miller     * Apply an animation to hide the incoming call widget.
58909337da1891f61ed2a821782f58f1b7d98770682Jim Miller     */
59009337da1891f61ed2a821782f58f1b7d98770682Jim Miller    private void hideIncomingCallWidget() {
5913964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller        if (mIncomingCallWidget.getVisibility() != View.VISIBLE
5923964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller                || mIncomingCallWidget.getAnimation() != null) {
5933964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller            // Widget is already hidden or in the process of being hidden
5943964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller            return;
5953964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller        }
5963964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller        // Hide the incoming call screen with a transition
59709337da1891f61ed2a821782f58f1b7d98770682Jim Miller        AlphaAnimation anim = new AlphaAnimation(1.0f, 0.0f);
59809337da1891f61ed2a821782f58f1b7d98770682Jim Miller        anim.setDuration(IN_CALL_WIDGET_TRANSITION_TIME);
59909337da1891f61ed2a821782f58f1b7d98770682Jim Miller        anim.setAnimationListener(new AnimationListener() {
60009337da1891f61ed2a821782f58f1b7d98770682Jim Miller
60109337da1891f61ed2a821782f58f1b7d98770682Jim Miller            public void onAnimationStart(Animation animation) {
60209337da1891f61ed2a821782f58f1b7d98770682Jim Miller
60309337da1891f61ed2a821782f58f1b7d98770682Jim Miller            }
60409337da1891f61ed2a821782f58f1b7d98770682Jim Miller
60509337da1891f61ed2a821782f58f1b7d98770682Jim Miller            public void onAnimationRepeat(Animation animation) {
60609337da1891f61ed2a821782f58f1b7d98770682Jim Miller
60709337da1891f61ed2a821782f58f1b7d98770682Jim Miller            }
60809337da1891f61ed2a821782f58f1b7d98770682Jim Miller
60909337da1891f61ed2a821782f58f1b7d98770682Jim Miller            public void onAnimationEnd(Animation animation) {
61009337da1891f61ed2a821782f58f1b7d98770682Jim Miller                // hide the incoming call UI.
6113964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller                mIncomingCallWidget.clearAnimation();
61209337da1891f61ed2a821782f58f1b7d98770682Jim Miller                mIncomingCallWidget.setVisibility(View.GONE);
61309337da1891f61ed2a821782f58f1b7d98770682Jim Miller            }
61409337da1891f61ed2a821782f58f1b7d98770682Jim Miller        });
61509337da1891f61ed2a821782f58f1b7d98770682Jim Miller        mIncomingCallWidget.startAnimation(anim);
61609337da1891f61ed2a821782f58f1b7d98770682Jim Miller    }
61709337da1891f61ed2a821782f58f1b7d98770682Jim Miller
61809337da1891f61ed2a821782f58f1b7d98770682Jim Miller    /**
6193964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller     * Shows the incoming call widget and cancels any animation that may be fading it out.
6203964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller     */
6213964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller    private void showIncomingCallWidget() {
6223964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller        Animation anim = mIncomingCallWidget.getAnimation();
6233964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller        if (anim != null) {
6243964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller            anim.reset();
6253964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller            mIncomingCallWidget.clearAnimation();
6263964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller        }
627b7ea5aff1b9f1dc965a9b9f6882b8ea951a3d8aeJim Miller        mIncomingCallWidget.reset(false);
6283964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller        mIncomingCallWidget.setVisibility(View.VISIBLE);
6293964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller    }
6303964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller
6313964d23925df0ff41d532952f3fddbe81d68dbdfJim Miller    /**
6323948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller     * Handles state changes of the SlidingTabSelector widget.  While the user
6339688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown     * is dragging one of the handles, we display an onscreen hint; see
6349688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown     * CallCard.getRotateWidgetHint().
6359688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown     */
6369688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown    public void onGrabbedStateChange(View v, int grabbedState) {
6379688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown        if (mInCallScreen != null) {
6389688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown            // Look up the hint based on which handle is currently grabbed.
6399688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown            // (Note we don't simply pass grabbedState thru to the InCallScreen,
6409688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown            // since *this* class is the only place that knows that the left
6419688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown            // handle means "Answer" and the right handle means "Decline".)
6429688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown            int hintTextResId, hintColorResId;
6439688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown            switch (grabbedState) {
6443948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                case SlidingTab.OnTriggerListener.NO_HANDLE:
6459688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    hintTextResId = 0;
6469688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    hintColorResId = 0;
6479688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    break;
6483948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                case SlidingTab.OnTriggerListener.LEFT_HANDLE:
6493948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                    // TODO: Use different variants of "Slide to answer" in some cases
6503948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                    // depending on the phone state, like slide_to_answer_and_hold
6513948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                    // for a call waiting call, or slide_to_answer_and_end_active or
6523948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                    // slide_to_answer_and_end_onhold for the 2-lines-in-use case.
6539688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    // (Note these are GSM-only cases, though.)
6543948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                    hintTextResId = R.string.slide_to_answer;
6559688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    hintColorResId = R.color.incall_textConnected;  // green
6569688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    break;
6573948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                case SlidingTab.OnTriggerListener.RIGHT_HANDLE:
6583948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller                    hintTextResId = R.string.slide_to_decline;
6599688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    hintColorResId = R.color.incall_textEnded;  // red
6609688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    break;
6619688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                default:
662825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                    Log.e(LOG_TAG, "onGrabbedStateChange: unexpected grabbedState: "
663825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                          + grabbedState);
6649688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    hintTextResId = 0;
6659688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    hintColorResId = 0;
6669688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown                    break;
6679688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown            }
6689688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown
6699688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown            // Tell the InCallScreen to update the CallCard and force the
6709688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown            // screen to redraw.
6713948a2541dd196b361d4cd9d18db805dd8cb56f9Jim Miller            mInCallScreen.updateSlidingTabHint(hintTextResId, hintColorResId);
6729688349b8750a9ac7282d83993e0112c5f51e7acDavid Brown        }
673a1bfb5406fa20be8351e272a089c582946e86fc4David Brown    }
674a1bfb5406fa20be8351e272a089c582946e86fc4David Brown
6754d910f9df60b820d3461995c1d85d0581041b531David Brown
676825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown    /**
677825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown     * OnTouchListener used to shrink the "hit target" of some onscreen
678825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown     * buttons.
679825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown     */
680825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown    class SmallerHitTargetTouchListener implements View.OnTouchListener {
681825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        /**
682825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         * Width of the allowable "hit target" as a percentage of
683825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         * the total width of this button.
684825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         */
685825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        private static final int HIT_TARGET_PERCENT_X = 50;
686825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
687825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        /**
688825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         * Height of the allowable "hit target" as a percentage of
689825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         * the total height of this button.
690825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         *
691825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         * This is larger than HIT_TARGET_PERCENT_X because some of
692825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         * the onscreen buttons are wide but not very tall and we don't
693825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         * want to make the vertical hit target *too* small.
694825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         */
695825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        private static final int HIT_TARGET_PERCENT_Y = 80;
696825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
697825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // Size (percentage-wise) of the "edge" area that's *not* touch-sensitive.
698825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        private static final int X_EDGE = (100 - HIT_TARGET_PERCENT_X) / 2;
699825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        private static final int Y_EDGE = (100 - HIT_TARGET_PERCENT_Y) / 2;
700825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // Min/max values (percentage-wise) of the touch-sensitive hit target.
701825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        private static final int X_HIT_MIN = X_EDGE;
702825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        private static final int X_HIT_MAX = 100 - X_EDGE;
703825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        private static final int Y_HIT_MIN = Y_EDGE;
704825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        private static final int Y_HIT_MAX = 100 - Y_EDGE;
705825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
706825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        // True if the most recent DOWN event was a "hit".
707825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        boolean mDownEventHit;
708825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
709825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        /**
710825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         * Called when a touch event is dispatched to a view. This allows listeners to
711825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         * get a chance to respond before the target view.
712825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         *
713825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         * @return True if the listener has consumed the event, false otherwise.
714825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         *         (In other words, we return true when the touch is *outside*
715825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         *         the "smaller hit target", which will prevent the actual
716825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         *         button from handling these events.)
717825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown         */
718825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        public boolean onTouch(View v, MotionEvent event) {
719825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown            // if (DBG) log("SmallerHitTargetTouchListener: " + v + ", event " + event);
720825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
721825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown            if (event.getAction() == MotionEvent.ACTION_DOWN) {
722825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // Note that event.getX() and event.getY() are already
723825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // translated into the View's coordinates.  (In other words,
724825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // "0,0" is a touch on the upper-left-most corner of the view.)
725825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                int touchX = (int) event.getX();
726825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                int touchY = (int) event.getY();
727825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
728825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                int viewWidth = v.getWidth();
729825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                int viewHeight = v.getHeight();
730825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
731825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // Touch location as a percentage of the total button width or height.
732825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                int touchXPercent = (int) ((float) (touchX * 100) / (float) viewWidth);
733825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                int touchYPercent = (int) ((float) (touchY * 100) / (float) viewHeight);
734825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // if (DBG) log("- percentage:  x = " + touchXPercent + ",  y = " + touchYPercent);
735825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
736825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // TODO: user research: add event logging here of the actual
737825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // hit location (and button ID), and enable it for dogfooders
738825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // for a few days.  That'll give us a good idea of how close
739825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // to the center of the button(s) most touch events are, to
740825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // help us fine-tune the HIT_TARGET_PERCENT_* constants.
741825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
742825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                if (touchXPercent < X_HIT_MIN || touchXPercent > X_HIT_MAX
743825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                        || touchYPercent < Y_HIT_MIN || touchYPercent > Y_HIT_MAX) {
744825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                    // Missed!
745825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                    // if (DBG) log("  -> MISSED!");
746825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                    mDownEventHit = false;
747825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                    return true;  // Consume this event; don't let the button see it
748825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                } else {
749825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                    // Hit!
750825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                    // if (DBG) log("  -> HIT!");
751825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                    mDownEventHit = true;
752825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                    return false;  // Let this event through to the actual button
753825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                }
754825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown            } else {
755825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // This is a MOVE, UP or CANCEL event.
756825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                //
757825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // We only do the "smaller hit target" check on DOWN events.
758825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // For the subsequent MOVE/UP/CANCEL events, we let them
759825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // through to the actual button IFF the previous DOWN event
760825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                // got through to the actual button (i.e. it was a "hit".)
761825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown                return !mDownEventHit;
762825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown            }
763825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown        }
764825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown    }
765825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
766825ccf147f31df0e00c22b6d66a63f2761533fdaDavid Brown
7674d910f9df60b820d3461995c1d85d0581041b531David Brown    // Debugging / testing code
7684d910f9df60b820d3461995c1d85d0581041b531David Brown
7694d910f9df60b820d3461995c1d85d0581041b531David Brown    private void log(String msg) {
7704d910f9df60b820d3461995c1d85d0581041b531David Brown        Log.d(LOG_TAG, msg);
7714d910f9df60b820d3461995c1d85d0581041b531David Brown    }
7724d910f9df60b820d3461995c1d85d0581041b531David Brown}
773