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