InCallTouchUi.java revision 1f3361d1fd6f0ee60362cecf686b7cf997ed485b
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;
204d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.os.SystemClock;
214d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.os.SystemProperties;
224d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.util.AttributeSet;
234d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.util.Log;
244d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.view.LayoutInflater;
254d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.view.MotionEvent;
264d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.view.View;
274d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.view.ViewConfiguration;
2847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brownimport android.widget.Button;
294d910f9df60b820d3461995c1d85d0581041b531David Brownimport android.widget.FrameLayout;
3047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brownimport android.widget.ToggleButton;
314d910f9df60b820d3461995c1d85d0581041b531David Brown
324d910f9df60b820d3461995c1d85d0581041b531David Brownimport com.android.internal.telephony.Call;
334d910f9df60b820d3461995c1d85d0581041b531David Brownimport com.android.internal.telephony.Phone;
344d910f9df60b820d3461995c1d85d0581041b531David Brown
354d910f9df60b820d3461995c1d85d0581041b531David Brown
364d910f9df60b820d3461995c1d85d0581041b531David Brown/**
374d910f9df60b820d3461995c1d85d0581041b531David Brown * In-call onscreen touch UI elements, used on some platforms.
384d910f9df60b820d3461995c1d85d0581041b531David Brown *
394d910f9df60b820d3461995c1d85d0581041b531David Brown * This widget is a fullscreen overlay, drawn on top of the
404d910f9df60b820d3461995c1d85d0581041b531David Brown * non-touch-sensitive parts of the in-call UI (i.e. the call card).
414d910f9df60b820d3461995c1d85d0581041b531David Brown */
424d910f9df60b820d3461995c1d85d0581041b531David Brownpublic class InCallTouchUi extends FrameLayout
434d910f9df60b820d3461995c1d85d0581041b531David Brown        implements View.OnClickListener, View.OnTouchListener {
444d910f9df60b820d3461995c1d85d0581041b531David Brown    private static final String LOG_TAG = "InCallTouchUi";
454d910f9df60b820d3461995c1d85d0581041b531David Brown    private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 1);
464d910f9df60b820d3461995c1d85d0581041b531David Brown
474d910f9df60b820d3461995c1d85d0581041b531David Brown    /**
484d910f9df60b820d3461995c1d85d0581041b531David Brown     * Reference to the InCallScreen activity that owns us.  This may be
494d910f9df60b820d3461995c1d85d0581041b531David Brown     * null if we haven't been initialized yet *or* after the InCallScreen
504d910f9df60b820d3461995c1d85d0581041b531David Brown     * activity has been destroyed.
514d910f9df60b820d3461995c1d85d0581041b531David Brown     */
524d910f9df60b820d3461995c1d85d0581041b531David Brown    private InCallScreen mInCallScreen;
534d910f9df60b820d3461995c1d85d0581041b531David Brown
544d910f9df60b820d3461995c1d85d0581041b531David Brown    // Phone app instance
554d910f9df60b820d3461995c1d85d0581041b531David Brown    private PhoneApp mApplication;
564d910f9df60b820d3461995c1d85d0581041b531David Brown
572b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    // UI containers / elements
584d910f9df60b820d3461995c1d85d0581041b531David Brown    private View mIncomingCallControls;  // UI elements used for an incoming call
594d910f9df60b820d3461995c1d85d0581041b531David Brown    private View mInCallControls;  // UI elements while on a regular call
6047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    //
6147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private Button mHoldButton;
6247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private Button mEndCallButton;
6347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private Button mDialpadButton;
6447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private ToggleButton mBluetoothButton;
6547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private ToggleButton mMuteButton;
6647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private ToggleButton mSpeakerButton;
6747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private Button mAddCallButton;
6847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private Button mMergeCallsButton;
6947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private Button mSwapCallsButton;
7047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown    private Button mManageConferenceButton;
714d910f9df60b820d3461995c1d85d0581041b531David Brown
724d910f9df60b820d3461995c1d85d0581041b531David Brown    // Double-tap detection state
734d910f9df60b820d3461995c1d85d0581041b531David Brown    private long mLastTouchTime;  // in SystemClock.uptimeMillis() time base
744d910f9df60b820d3461995c1d85d0581041b531David Brown    private View mLastTouchView;
754d910f9df60b820d3461995c1d85d0581041b531David Brown
764d910f9df60b820d3461995c1d85d0581041b531David Brown    // Overall enabledness of the "touch UI" features
774d910f9df60b820d3461995c1d85d0581041b531David Brown    private boolean mAllowIncomingCallTouchUi;
784d910f9df60b820d3461995c1d85d0581041b531David Brown    private boolean mAllowInCallTouchUi;
794d910f9df60b820d3461995c1d85d0581041b531David Brown
804d910f9df60b820d3461995c1d85d0581041b531David Brown    public InCallTouchUi(Context context, AttributeSet attrs) {
814d910f9df60b820d3461995c1d85d0581041b531David Brown        super(context, attrs);
824d910f9df60b820d3461995c1d85d0581041b531David Brown
834d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("InCallTouchUi constructor...");
844d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("- this = " + this);
854d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("- context " + context + ", attrs " + attrs);
864d910f9df60b820d3461995c1d85d0581041b531David Brown
874d910f9df60b820d3461995c1d85d0581041b531David Brown        // Inflate our contents, and add it (to ourself) as a child.
884d910f9df60b820d3461995c1d85d0581041b531David Brown        LayoutInflater inflater = LayoutInflater.from(context);
894d910f9df60b820d3461995c1d85d0581041b531David Brown        inflater.inflate(
904d910f9df60b820d3461995c1d85d0581041b531David Brown                R.layout.incall_touch_ui,  // resource
914d910f9df60b820d3461995c1d85d0581041b531David Brown                this,                      // root
924d910f9df60b820d3461995c1d85d0581041b531David Brown                true);
934d910f9df60b820d3461995c1d85d0581041b531David Brown
944d910f9df60b820d3461995c1d85d0581041b531David Brown        mApplication = PhoneApp.getInstance();
954d910f9df60b820d3461995c1d85d0581041b531David Brown
964d910f9df60b820d3461995c1d85d0581041b531David Brown        // The various touch UI features are enabled on a per-product
974d910f9df60b820d3461995c1d85d0581041b531David Brown        // basis.  (These flags in config.xml may be overridden by
984d910f9df60b820d3461995c1d85d0581041b531David Brown        // product-specific overlay files.)
994d910f9df60b820d3461995c1d85d0581041b531David Brown
1004d910f9df60b820d3461995c1d85d0581041b531David Brown        mAllowIncomingCallTouchUi = getResources().getBoolean(R.bool.allow_incoming_call_touch_ui);
1014d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("- incoming call touch UI: "
1024d910f9df60b820d3461995c1d85d0581041b531David Brown                     + (mAllowIncomingCallTouchUi ? "ENABLED" : "DISABLED"));
1034d910f9df60b820d3461995c1d85d0581041b531David Brown        mAllowInCallTouchUi = getResources().getBoolean(R.bool.allow_in_call_touch_ui);
1044d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("- regular in-call touch UI: "
1054d910f9df60b820d3461995c1d85d0581041b531David Brown                     + (mAllowInCallTouchUi ? "ENABLED" : "DISABLED"));
1064d910f9df60b820d3461995c1d85d0581041b531David Brown    }
1074d910f9df60b820d3461995c1d85d0581041b531David Brown
1084d910f9df60b820d3461995c1d85d0581041b531David Brown    void setInCallScreenInstance(InCallScreen inCallScreen) {
1094d910f9df60b820d3461995c1d85d0581041b531David Brown        mInCallScreen = inCallScreen;
1104d910f9df60b820d3461995c1d85d0581041b531David Brown    }
1114d910f9df60b820d3461995c1d85d0581041b531David Brown
1124d910f9df60b820d3461995c1d85d0581041b531David Brown    @Override
1134d910f9df60b820d3461995c1d85d0581041b531David Brown    protected void onFinishInflate() {
1144d910f9df60b820d3461995c1d85d0581041b531David Brown        super.onFinishInflate();
1154d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("InCallTouchUi onFinishInflate(this = " + this + ")...");
1164d910f9df60b820d3461995c1d85d0581041b531David Brown
1174d910f9df60b820d3461995c1d85d0581041b531David Brown        // Look up the various UI elements.
1184d910f9df60b820d3461995c1d85d0581041b531David Brown
1194d910f9df60b820d3461995c1d85d0581041b531David Brown        // The containers:
1204d910f9df60b820d3461995c1d85d0581041b531David Brown        mIncomingCallControls = findViewById(R.id.incomingCallControls);
1214d910f9df60b820d3461995c1d85d0581041b531David Brown        mInCallControls = findViewById(R.id.inCallControls);
1224d910f9df60b820d3461995c1d85d0581041b531David Brown
1232b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        // "Double-tap" buttons, where we listen for raw touch events
1242b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        // and possibly turn them into double-tap events:
1252b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        mIncomingCallControls.findViewById(R.id.answerButton).setOnTouchListener(this);
1262b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        mIncomingCallControls.findViewById(R.id.rejectButton).setOnTouchListener(this);
1272b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
1282b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        // Regular (single-tap) buttons, where we listen for click events:
12947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // Main cluster of buttons:
13047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mHoldButton = (Button) mInCallControls.findViewById(R.id.holdButton);
13147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mHoldButton.setOnClickListener(this);
13247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mEndCallButton = (Button) mInCallControls.findViewById(R.id.endCallButton);
13347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mEndCallButton.setOnClickListener(this);
13447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mDialpadButton = (Button) mInCallControls.findViewById(R.id.dialpadButton);
13547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mDialpadButton.setOnClickListener(this);
13647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mBluetoothButton = (ToggleButton) mInCallControls.findViewById(R.id.bluetoothButton);
13747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mBluetoothButton.setOnClickListener(this);
13847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMuteButton = (ToggleButton) mInCallControls.findViewById(R.id.muteButton);
13947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMuteButton.setOnClickListener(this);
14047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSpeakerButton = (ToggleButton) mInCallControls.findViewById(R.id.speakerButton);
14147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSpeakerButton.setOnClickListener(this);
14247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // Upper corner buttons:
14347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mAddCallButton = (Button) mInCallControls.findViewById(R.id.addCallButton);
14447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mAddCallButton.setOnClickListener(this);
14547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMergeCallsButton = (Button) mInCallControls.findViewById(R.id.mergeCallsButton);
14647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMergeCallsButton.setOnClickListener(this);
14747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSwapCallsButton = (Button) mInCallControls.findViewById(R.id.swapCallsButton);
14847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSwapCallsButton.setOnClickListener(this);
14947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mManageConferenceButton =
15047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown                (Button) mInCallControls.findViewById(R.id.manageConferenceButton);
15147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mManageConferenceButton.setOnClickListener(this);
1524d910f9df60b820d3461995c1d85d0581041b531David Brown    }
1534d910f9df60b820d3461995c1d85d0581041b531David Brown
1544d910f9df60b820d3461995c1d85d0581041b531David Brown    /**
1554d910f9df60b820d3461995c1d85d0581041b531David Brown     * Updates the visibility and/or state of our UI elements, based on
1564d910f9df60b820d3461995c1d85d0581041b531David Brown     * the current state of the phone.
1574d910f9df60b820d3461995c1d85d0581041b531David Brown     */
1584d910f9df60b820d3461995c1d85d0581041b531David Brown    void updateState(Phone phone) {
1594d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("updateState(" + phone + ")...");
1604d910f9df60b820d3461995c1d85d0581041b531David Brown
1611f3361d1fd6f0ee60362cecf686b7cf997ed485bDavid Brown        if (mInCallScreen == null) {
1621f3361d1fd6f0ee60362cecf686b7cf997ed485bDavid Brown            log("- updateState: mInCallScreen has been destroyed; bailing out...");
1631f3361d1fd6f0ee60362cecf686b7cf997ed485bDavid Brown            return;
1641f3361d1fd6f0ee60362cecf686b7cf997ed485bDavid Brown        }
1651f3361d1fd6f0ee60362cecf686b7cf997ed485bDavid Brown
1664d910f9df60b820d3461995c1d85d0581041b531David Brown        Phone.State state = phone.getState();  // IDLE, RINGING, or OFFHOOK
1674d910f9df60b820d3461995c1d85d0581041b531David Brown
1684d910f9df60b820d3461995c1d85d0581041b531David Brown        boolean showIncomingCallControls = false;
1694d910f9df60b820d3461995c1d85d0581041b531David Brown        boolean showInCallControls = false;
1704d910f9df60b820d3461995c1d85d0581041b531David Brown
1714d910f9df60b820d3461995c1d85d0581041b531David Brown        if (state == Phone.State.RINGING) {
1724d910f9df60b820d3461995c1d85d0581041b531David Brown            // A phone call is ringing *or* call waiting.
1734d910f9df60b820d3461995c1d85d0581041b531David Brown            if (mAllowIncomingCallTouchUi) {
1744d910f9df60b820d3461995c1d85d0581041b531David Brown                if (DBG) log("- updateState: RINGING!  Showing incoming call controls...");
1754d910f9df60b820d3461995c1d85d0581041b531David Brown                showIncomingCallControls = true;
1764d910f9df60b820d3461995c1d85d0581041b531David Brown
1774d910f9df60b820d3461995c1d85d0581041b531David Brown                // TODO: UI design issue: if the device is NOT currently
1784d910f9df60b820d3461995c1d85d0581041b531David Brown                // locked, we probably don't need to make the user
1794d910f9df60b820d3461995c1d85d0581041b531David Brown                // double-tap the "incoming call" buttons.  (The device
1804d910f9df60b820d3461995c1d85d0581041b531David Brown                // presumably isn't in a pocket or purse, so we don't need
1814d910f9df60b820d3461995c1d85d0581041b531David Brown                // to worry about false touches while it's ringing.)
1824d910f9df60b820d3461995c1d85d0581041b531David Brown                // But OTOH having "inconsistent" buttons might just make
1834d910f9df60b820d3461995c1d85d0581041b531David Brown                // it *more* confusing.
1844d910f9df60b820d3461995c1d85d0581041b531David Brown            }
1854d910f9df60b820d3461995c1d85d0581041b531David Brown        } else {
1864d910f9df60b820d3461995c1d85d0581041b531David Brown            if (mAllowInCallTouchUi) {
1872b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // Ok, the in-call touch UI is available on this platform,
1882b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // so make it visible (with some exceptions):
1892b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                if (mInCallScreen.okToShowInCallTouchUi()) {
1902b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                    showInCallControls = true;
1912b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                } else {
1922b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                    if (DBG) log("- updateState: NOT OK to show touch UI; disabling...");
1932b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                }
1944d910f9df60b820d3461995c1d85d0581041b531David Brown            }
1954d910f9df60b820d3461995c1d85d0581041b531David Brown        }
1964d910f9df60b820d3461995c1d85d0581041b531David Brown
1972b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        if (showInCallControls) {
1982b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            updateInCallControls(phone);
1992b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        }
2002b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
2014d910f9df60b820d3461995c1d85d0581041b531David Brown        if (showIncomingCallControls && showInCallControls) {
2024d910f9df60b820d3461995c1d85d0581041b531David Brown            throw new IllegalStateException(
2034d910f9df60b820d3461995c1d85d0581041b531David Brown                "'Incoming' and 'in-call' touch controls visible at the same time!");
2044d910f9df60b820d3461995c1d85d0581041b531David Brown        }
2054d910f9df60b820d3461995c1d85d0581041b531David Brown        mIncomingCallControls.setVisibility(showIncomingCallControls ? View.VISIBLE : View.GONE);
2064d910f9df60b820d3461995c1d85d0581041b531David Brown        mInCallControls.setVisibility(showInCallControls ? View.VISIBLE : View.GONE);
2074d910f9df60b820d3461995c1d85d0581041b531David Brown
2084d910f9df60b820d3461995c1d85d0581041b531David Brown        // TODO: As an optimization, also consider setting the visibility
20947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // of the overall InCallTouchUi widget to GONE if *nothing at all*
2104d910f9df60b820d3461995c1d85d0581041b531David Brown        // is visible right now.
2114d910f9df60b820d3461995c1d85d0581041b531David Brown    }
2124d910f9df60b820d3461995c1d85d0581041b531David Brown
2134d910f9df60b820d3461995c1d85d0581041b531David Brown    // View.OnClickListener implementation
2144d910f9df60b820d3461995c1d85d0581041b531David Brown    public void onClick(View view) {
2154d910f9df60b820d3461995c1d85d0581041b531David Brown        int id = view.getId();
2164d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("onClick(View " + view + ", id " + id + ")...");
2174d910f9df60b820d3461995c1d85d0581041b531David Brown
2182b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        switch (id) {
2192b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.answerButton:
2202b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.rejectButton:
2212b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // These are "double-tap" buttons; we should never get regular
2222b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // clicks from them.
2232b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                Log.w(LOG_TAG, "onClick: unexpected click: View " + view + ", id " + id);
2242b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                throw new IllegalStateException("Unexpected click from double-tap button");
2252b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // TODO: remove above "throw" after initial debugging.
2262b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // break;
2272b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
2282b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.holdButton:
2292b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.endCallButton:
2302b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.dialpadButton:
2312b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.bluetoothButton:
2322b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.muteButton:
2332b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            case R.id.speakerButton:
23447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown            case R.id.addCallButton:
23547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown            case R.id.mergeCallsButton:
23647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown            case R.id.swapCallsButton:
23747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown            case R.id.manageConferenceButton:
2382b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // Clicks on the regular onscreen buttons get forwarded
2392b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // straight to the InCallScreen.
2402b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                mInCallScreen.handleOnscreenButtonClick(id);
2412b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                break;
2422b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
2432b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown            default:
2442b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                if (DBG) log("onClick: unexpected click: View " + view + ", id " + id);
2452b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                throw new IllegalStateException("Unexpected click event");
2462b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // TODO: remove above "throw" after initial debugging.
2472b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // break;
2482b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        }
2494d910f9df60b820d3461995c1d85d0581041b531David Brown    }
2504d910f9df60b820d3461995c1d85d0581041b531David Brown
2514d910f9df60b820d3461995c1d85d0581041b531David Brown    private void onDoubleTap(View view) {
2524d910f9df60b820d3461995c1d85d0581041b531David Brown        int id = view.getId();
2534d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log("onDoubleTap(View " + view + ", id " + id + ")...");
2544d910f9df60b820d3461995c1d85d0581041b531David Brown
2554d910f9df60b820d3461995c1d85d0581041b531David Brown        switch (id) {
2564d910f9df60b820d3461995c1d85d0581041b531David Brown            case R.id.answerButton:
2574d910f9df60b820d3461995c1d85d0581041b531David Brown            case R.id.rejectButton:
2582b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // Pass these clicks on to the InCallScreen.
2592b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                mInCallScreen.handleOnscreenButtonClick(id);
2604d910f9df60b820d3461995c1d85d0581041b531David Brown                break;
2614d910f9df60b820d3461995c1d85d0581041b531David Brown
2624d910f9df60b820d3461995c1d85d0581041b531David Brown            default:
2632b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                Log.w(LOG_TAG, "onDoubleTap: unexpected double-tap: View " + view + ", id " + id);
2642b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                throw new IllegalStateException("Unexpected double-tap event");
2652b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // TODO: remove above "throw" after initial debugging.
2662b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown                // break;
2674d910f9df60b820d3461995c1d85d0581041b531David Brown        }
2684d910f9df60b820d3461995c1d85d0581041b531David Brown    }
2694d910f9df60b820d3461995c1d85d0581041b531David Brown
2704d910f9df60b820d3461995c1d85d0581041b531David Brown    // View.OnTouchListener implementation
2714d910f9df60b820d3461995c1d85d0581041b531David Brown    public boolean onTouch(View v, MotionEvent event) {
2724d910f9df60b820d3461995c1d85d0581041b531David Brown        if (DBG) log ("onTouch(View " + v + ")...");
2734d910f9df60b820d3461995c1d85d0581041b531David Brown
2744d910f9df60b820d3461995c1d85d0581041b531David Brown        // Look for double-tap events.
2754d910f9df60b820d3461995c1d85d0581041b531David Brown
2764d910f9df60b820d3461995c1d85d0581041b531David Brown        // TODO: it's a little ugly to do the double-tap detection right
2774d910f9df60b820d3461995c1d85d0581041b531David Brown        // here; consider extracting it out to a helper class that can
2784d910f9df60b820d3461995c1d85d0581041b531David Brown        // listen for double-taps on arbitrary View(s), or maybe even
2794d910f9df60b820d3461995c1d85d0581041b531David Brown        // a whole new "DoubleTapButton" widget.
2804d910f9df60b820d3461995c1d85d0581041b531David Brown
2814d910f9df60b820d3461995c1d85d0581041b531David Brown        if (event.getAction() == MotionEvent.ACTION_DOWN) {
2824d910f9df60b820d3461995c1d85d0581041b531David Brown            long now = SystemClock.uptimeMillis();
2834d910f9df60b820d3461995c1d85d0581041b531David Brown            if (DBG) log("- InCallTouchUi: handling a DOWN event, t = " + now);
2844d910f9df60b820d3461995c1d85d0581041b531David Brown
2854d910f9df60b820d3461995c1d85d0581041b531David Brown            // Look for a double-tap on a particular View:
2864d910f9df60b820d3461995c1d85d0581041b531David Brown            if ((v == mLastTouchView)
2874d910f9df60b820d3461995c1d85d0581041b531David Brown                && (now < (mLastTouchTime
2884d910f9df60b820d3461995c1d85d0581041b531David Brown                           + ViewConfiguration.getDoubleTapTimeout()))) {
2894d910f9df60b820d3461995c1d85d0581041b531David Brown                if (DBG) log("==> InCallTouchUi: DOUBLE-TAP!");
2904d910f9df60b820d3461995c1d85d0581041b531David Brown                onDoubleTap(v);
2914d910f9df60b820d3461995c1d85d0581041b531David Brown
2924d910f9df60b820d3461995c1d85d0581041b531David Brown                // Since we (presumably) just answered or rejected an
2934d910f9df60b820d3461995c1d85d0581041b531David Brown                // incoming call, that means the phone state is about to
2944d910f9df60b820d3461995c1d85d0581041b531David Brown                // change.  The in-call touch UI will update itself as
2954d910f9df60b820d3461995c1d85d0581041b531David Brown                // soon as the phone state change event gets back to the
2964d910f9df60b820d3461995c1d85d0581041b531David Brown                // InCallScreen (which will ultimately trigger an
2974d910f9df60b820d3461995c1d85d0581041b531David Brown                // updateScreen() call here.)
2984d910f9df60b820d3461995c1d85d0581041b531David Brown                // TODO: But also, consider explicitly starting some
2994d910f9df60b820d3461995c1d85d0581041b531David Brown                // fancier animation here, like fading out the
3004d910f9df60b820d3461995c1d85d0581041b531David Brown                // answer/decline buttons, or sliding them offscreen...
3014d910f9df60b820d3461995c1d85d0581041b531David Brown            }
3024d910f9df60b820d3461995c1d85d0581041b531David Brown        } else if (event.getAction() == MotionEvent.ACTION_UP) {
3034d910f9df60b820d3461995c1d85d0581041b531David Brown            // Stash away the view that was touched and the current
3044d910f9df60b820d3461995c1d85d0581041b531David Brown            // time in case this is the first tap of a double-tap
3054d910f9df60b820d3461995c1d85d0581041b531David Brown            // gesture.  (We measure the time from the first tap's UP
3064d910f9df60b820d3461995c1d85d0581041b531David Brown            // to the second tap's DOWN.)
3074d910f9df60b820d3461995c1d85d0581041b531David Brown            mLastTouchTime = SystemClock.uptimeMillis();
3084d910f9df60b820d3461995c1d85d0581041b531David Brown            mLastTouchView = v;
3094d910f9df60b820d3461995c1d85d0581041b531David Brown        }
3104d910f9df60b820d3461995c1d85d0581041b531David Brown
3114d910f9df60b820d3461995c1d85d0581041b531David Brown        // And regardless of what just happened, we *always*
3124d910f9df60b820d3461995c1d85d0581041b531David Brown        // consume touch events here.
3134d910f9df60b820d3461995c1d85d0581041b531David Brown        return true;
3144d910f9df60b820d3461995c1d85d0581041b531David Brown    }
3154d910f9df60b820d3461995c1d85d0581041b531David Brown
3162b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    /**
31747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown     * Updates the enabledness and "checked" state of the buttons on the
3182b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown     * "inCallControls" panel, based on the current telephony state.
3192b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown     */
3202b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    void updateInCallControls(Phone phone) {
32147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // Note we do NOT need to worry here about cases where the entire
32247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // in-call touch UI is disabled, like during an OTA call or if the
32347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // dtmf dialpad is up.  (That's handled by updateState(), which
32447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // calls InCallScreen.okToShowInCallTouchUi().)
32547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        //
32647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // If we get here, it *is* OK to show the in-call touch UI, so we
32747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // now need to update the enabledness and/or "checked" state of
32847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // each individual button.
32947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        //
33047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
33147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // The InCallControlState object tells us the enabledness and/or
33247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // state of the various onscreen buttons:
33347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        InCallControlState inCallControlState = mInCallScreen.getUpdatedInCallControlState();
33447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
33547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Hold"
33647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // Note: for now "Hold" isn't a ToggleButton, so we don't need to
33747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // update its "checked" state.  Just enable or disable it:
33847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mHoldButton.setEnabled(inCallControlState.canHold);
33947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
34047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "End call": this button has no state and it's always enabled.
34147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mEndCallButton.setEnabled(true);
34247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
34347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Dialpad": Enabled only when it's OK to use the dialpad in the
34447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // first place.
34547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mDialpadButton.setEnabled(inCallControlState.dialpadEnabled);
34647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
34747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Bluetooth"
34847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mBluetoothButton.setEnabled(inCallControlState.bluetoothEnabled);
34947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mBluetoothButton.setChecked(inCallControlState.bluetoothIndicatorOn);
35047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
35147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Mute"
35247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMuteButton.setEnabled(inCallControlState.canMute);
35347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMuteButton.setChecked(inCallControlState.muteIndicatorOn);
35447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
35547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Speaker"
35647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSpeakerButton.setEnabled(inCallControlState.speakerEnabled);
35747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSpeakerButton.setChecked(inCallControlState.speakerOn);
35847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
35947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Add call"
36047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // (Note "add call" and "merge calls" are never both available at
36147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // the same time.  That's why it's OK for them to both be in the
36247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // same position onscreen.)
36347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // This button is totally hidden (rather than just disabled)
36447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // when the operation isn't available.
36547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mAddCallButton.setVisibility(inCallControlState.canAddCall ? View.VISIBLE : View.GONE);
36647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
36747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Merge calls"
36847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // This button is totally hidden (rather than just disabled)
36947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // when the operation isn't available.
37047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mMergeCallsButton.setVisibility(inCallControlState.canMerge ? View.VISIBLE : View.GONE);
37147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
37247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Swap calls"
37347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // This button is totally hidden (rather than just disabled)
37447e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // when the operation isn't available.
37547e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mSwapCallsButton.setVisibility(inCallControlState.canSwap ? View.VISIBLE : View.GONE);
37647e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown
37747e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // "Manage conference"
37847e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // This button is totally hidden (rather than just disabled)
37947e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        // when the operation isn't available.
38047e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        boolean showManageConferenceTouchButton = inCallControlState.manageConferenceVisible
38147e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown                && inCallControlState.manageConferenceEnabled;
38247e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown        mManageConferenceButton.setVisibility(showManageConferenceTouchButton
38347e534e7b9f75d8ec85ba15f1fd681770ec4592eDavid Brown                                              ? View.VISIBLE : View.GONE);
3842b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    }
3852b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
3862b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
3872b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    //
3882b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    // InCallScreen API
3892b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    //
3902b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
3912b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    /**
3922b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown     * @return true if the onscreen touch UI is enabled (for regular
3932b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown     * "ongoing call" states) on the current device.
3942b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown     */
3952b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    public boolean isTouchUiEnabled() {
3962b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown        return mAllowInCallTouchUi;
3972b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown    }
3982b7613e21b0bea57cfa4e5ba5860a8ff1cfefbc1David Brown
3994d910f9df60b820d3461995c1d85d0581041b531David Brown
4004d910f9df60b820d3461995c1d85d0581041b531David Brown    // Debugging / testing code
4014d910f9df60b820d3461995c1d85d0581041b531David Brown
4024d910f9df60b820d3461995c1d85d0581041b531David Brown    private void log(String msg) {
4034d910f9df60b820d3461995c1d85d0581041b531David Brown        Log.d(LOG_TAG, msg);
4044d910f9df60b820d3461995c1d85d0581041b531David Brown    }
4054d910f9df60b820d3461995c1d85d0581041b531David Brown
4064d910f9df60b820d3461995c1d85d0581041b531David Brown    private static void logErr(String msg) {
4074d910f9df60b820d3461995c1d85d0581041b531David Brown        Log.e(LOG_TAG, msg);
4084d910f9df60b820d3461995c1d85d0581041b531David Brown    }
4094d910f9df60b820d3461995c1d85d0581041b531David Brown}
410