diff options
Diffstat (limited to 'InCallUI/src/com/android/incallui/CallButtonFragment.java')
-rw-r--r-- | InCallUI/src/com/android/incallui/CallButtonFragment.java | 288 |
1 files changed, 272 insertions, 16 deletions
diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java index 772251dfa..175fe4d95 100644 --- a/InCallUI/src/com/android/incallui/CallButtonFragment.java +++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java @@ -17,39 +17,48 @@ package com.android.incallui; import android.content.Context; +import android.graphics.drawable.LayerDrawable; import android.media.AudioManager; import android.os.Bundle; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnDismissListener; +import android.widget.PopupMenu.OnMenuItemClickListener; import android.widget.ToggleButton; +import com.android.services.telephony.common.AudioMode; + /** * Fragment for call control buttons */ public class CallButtonFragment extends BaseFragment<CallButtonPresenter> - implements CallButtonPresenter.CallButtonUi { + implements CallButtonPresenter.CallButtonUi, OnMenuItemClickListener, + OnDismissListener { private ToggleButton mMuteButton; private ToggleButton mAudioButton; private ToggleButton mHoldButton; private ToggleButton mShowDialpadButton; + private PopupMenu mAudioModePopup; + private boolean mAudioModePopupVisible; private View mEndCallButton; @Override CallButtonPresenter createPresenter() { - return new CallButtonPresenter(); + // TODO: find a cleaner way to include audio mode provider than + // having a singleton instance. + return new CallButtonPresenter(AudioModeProvider.getInstance()); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - final AudioManager audioManager = (AudioManager) getActivity().getSystemService( - Context.AUDIO_SERVICE); - getPresenter().init(audioManager); } @Override @@ -74,10 +83,10 @@ public class CallButtonFragment extends BaseFragment<CallButtonPresenter> }); mAudioButton = (ToggleButton) parent.findViewById(R.id.audioButton); - mAudioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + mAudioButton.setOnClickListener(new View.OnClickListener() { @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - getPresenter().speakerClicked(isChecked); + public void onClick(View view) { + onAudioButtonClicked(); } }); @@ -103,6 +112,14 @@ public class CallButtonFragment extends BaseFragment<CallButtonPresenter> @Override public void onViewCreated(View view, Bundle savedInstanceState) { getPresenter().onUiReady(this); + + // set the buttons + updateAudioButtons(getPresenter().getSupportedAudio()); + } + + @Override + public void onDestroyView() { + getPresenter().onUiUnready(this); } @Override @@ -119,17 +136,256 @@ public class CallButtonFragment extends BaseFragment<CallButtonPresenter> mMuteButton.setChecked(value); } - /** - * TODO(klp): Rename this from setSpeaker() to setAudio() once it does more than speakerphone. - */ @Override - public void setSpeaker(boolean value) { - mAudioButton.setChecked(value); + public void setHold(boolean value) { + mHoldButton.setChecked(value); } @Override - public void setHold(boolean value) { - mHoldButton.setChecked(value); + public void setAudio(int mode) { + } + + @Override + public void setSupportedAudio(int modeMask) { + updateAudioButtons(modeMask); + refreshAudioModePopup(); + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + Logger.d(this, "- onMenuItemClick: " + item); + Logger.d(this, " id: " + item.getItemId()); + Logger.d(this, " title: '" + item.getTitle() + "'"); + + int mode = AudioMode.WIRED_OR_EARPIECE; + + switch (item.getItemId()) { + case R.id.audio_mode_speaker: + mode = AudioMode.SPEAKER; + break; + case R.id.audio_mode_earpiece: + case R.id.audio_mode_wired_headset: + // InCallAudioMode.EARPIECE means either the handset earpiece, + // or the wired headset (if connected.) + mode = AudioMode.WIRED_OR_EARPIECE; + break; + case R.id.audio_mode_bluetooth: + mode = AudioMode.BLUETOOTH; + break; + default: + Logger.e(this, "onMenuItemClick: unexpected View ID " + item.getItemId() + + " (MenuItem = '" + item + "')"); + break; + } + + getPresenter().setAudioMode(mode); + + return true; + } + + // PopupMenu.OnDismissListener implementation; see showAudioModePopup(). + // This gets called when the PopupMenu gets dismissed for *any* reason, like + // the user tapping outside its bounds, or pressing Back, or selecting one + // of the menu items. + @Override + public void onDismiss(PopupMenu menu) { + Logger.d(this, "- onDismiss: " + menu); + mAudioModePopupVisible = false; + } + + /** + * Checks for supporting modes. If bluetooth is supported, it uses the audio + * pop up menu. Otherwise, it toggles the speakerphone. + */ + private void onAudioButtonClicked() { + Logger.d(this, "onAudioButtonClicked: " + + AudioMode.toString(getPresenter().getSupportedAudio())); + + if (isSupported(AudioMode.BLUETOOTH)) { + showAudioModePopup(); + } else { + getPresenter().toggleSpeakerphone(); + } + } + + /** + * Refreshes the "Audio mode" popup if it's visible. This is useful + * (for example) when a wired headset is plugged or unplugged, + * since we need to switch back and forth between the "earpiece" + * and "wired headset" items. + * + * This is safe to call even if the popup is already dismissed, or even if + * you never called showAudioModePopup() in the first place. + */ + public void refreshAudioModePopup() { + if (mAudioModePopup != null && mAudioModePopupVisible) { + // Dismiss the previous one + mAudioModePopup.dismiss(); // safe even if already dismissed + // And bring up a fresh PopupMenu + showAudioModePopup(); + } + } + + /** + * Updates the audio button so that the appriopriate visual layers + * are visible based on the supported audio formats. + */ + private void updateAudioButtons(int supportedModes) { + final boolean bluetoothSupported = isSupported(AudioMode.BLUETOOTH); + final boolean speakerSupported = isSupported(AudioMode.SPEAKER); + + boolean audioButtonEnabled = false; + boolean audioButtonChecked = false; + boolean showMoreIndicator = false; + + boolean showBluetoothIcon = false; + boolean showSpeakerphoneOnIcon = false; + boolean showSpeakerphoneOffIcon = false; + boolean showHandsetIcon = false; + + boolean showToggleIndicator = false; + + if (bluetoothSupported) { + Logger.d(this, "updateAudioButtons - popup menu mode"); + + audioButtonEnabled = true; + showMoreIndicator = true; + // The audio button is NOT a toggle in this state. (And its + // setChecked() state is irrelevant since we completely hide the + // btn_compound_background layer anyway.) + + // Update desired layers: + if (isAudio(AudioMode.BLUETOOTH)) { + showBluetoothIcon = true; + } else if (isAudio(AudioMode.SPEAKER)) { + showSpeakerphoneOnIcon = true; + } else { + showHandsetIcon = true; + // TODO: if a wired headset is plugged in, that takes precedence + // over the handset earpiece. If so, maybe we should show some + // sort of "wired headset" icon here instead of the "handset + // earpiece" icon. (Still need an asset for that, though.) + } + } else if (speakerSupported) { + Logger.d(this, "updateAudioButtons - speaker toggle mode"); + + audioButtonEnabled = true; + + // The audio button *is* a toggle in this state, and indicated the + // current state of the speakerphone. + audioButtonChecked = isAudio(AudioMode.SPEAKER); + + // update desired layers: + showToggleIndicator = true; + + showSpeakerphoneOnIcon = isAudio(AudioMode.SPEAKER); + showSpeakerphoneOffIcon = !showSpeakerphoneOnIcon; + } else { + Logger.d(this, "updateAudioButtons - disabled..."); + + // The audio button is a toggle in this state, but that's mostly + // irrelevant since it's always disabled and unchecked. + audioButtonEnabled = false; + audioButtonChecked = false; + + // update desired layers: + showToggleIndicator = true; + showSpeakerphoneOffIcon = true; + } + + // Finally, update it all! + + Logger.v(this, "audioButtonEnabled: " + audioButtonEnabled); + Logger.v(this, "audioButtonChecked: " + audioButtonChecked); + Logger.v(this, "showMoreIndicator: " + showMoreIndicator); + Logger.v(this, "showBluetoothIcon: " + showBluetoothIcon); + Logger.v(this, "showSpeakerphoneOnIcon: " + showSpeakerphoneOnIcon); + Logger.v(this, "showSpeakerphoneOffIcon: " + showSpeakerphoneOffIcon); + Logger.v(this, "showHandsetIcon: " + showHandsetIcon); + + // Constants for Drawable.setAlpha() + final int HIDDEN = 0; + final int VISIBLE = 255; + + mAudioButton.setEnabled(audioButtonEnabled); + mAudioButton.setChecked(audioButtonChecked); + + final LayerDrawable layers = (LayerDrawable) mAudioButton.getBackground(); + Logger.d(this, "'layers' drawable: " + layers); + + layers.findDrawableByLayerId(R.id.compoundBackgroundItem) + .setAlpha(showToggleIndicator ? VISIBLE : HIDDEN); + + layers.findDrawableByLayerId(R.id.moreIndicatorItem) + .setAlpha(showMoreIndicator ? VISIBLE : HIDDEN); + + layers.findDrawableByLayerId(R.id.bluetoothItem) + .setAlpha(showBluetoothIcon ? VISIBLE : HIDDEN); + + layers.findDrawableByLayerId(R.id.handsetItem) + .setAlpha(showHandsetIcon ? VISIBLE : HIDDEN); + + layers.findDrawableByLayerId(R.id.speakerphoneOnItem) + .setAlpha(showSpeakerphoneOnIcon ? VISIBLE : HIDDEN); + + layers.findDrawableByLayerId(R.id.speakerphoneOffItem) + .setAlpha(showSpeakerphoneOffIcon ? VISIBLE : HIDDEN); + } + + private void showAudioModePopup() { + Logger.d(this, "showAudioPopup()..."); + + mAudioModePopup = new PopupMenu(getView().getContext(), mAudioButton /* anchorView */); + mAudioModePopup.getMenuInflater().inflate(R.menu.incall_audio_mode_menu, + mAudioModePopup.getMenu()); + mAudioModePopup.setOnMenuItemClickListener(this); + mAudioModePopup.setOnDismissListener(this); + + final Menu menu = mAudioModePopup.getMenu(); + + // TODO: Still need to have the "currently active" audio mode come + // up pre-selected (or focused?) with a blue highlight. Still + // need exact visual design, and possibly framework support for this. + // See comments below for the exact logic. + + final MenuItem speakerItem = menu.findItem(R.id.audio_mode_speaker); + speakerItem.setEnabled(isSupported(AudioMode.SPEAKER)); + // TODO: Show speakerItem as initially "selected" if + // speaker is on. + + // We display *either* "earpiece" or "wired headset", never both, + // depending on whether a wired headset is physically plugged in. + final MenuItem earpieceItem = menu.findItem(R.id.audio_mode_earpiece); + final MenuItem wiredHeadsetItem = menu.findItem(R.id.audio_mode_wired_headset); + + final boolean usingHeadset = isSupported(AudioMode.WIRED_HEADSET); + earpieceItem.setVisible(!usingHeadset); + earpieceItem.setEnabled(!usingHeadset); + wiredHeadsetItem.setVisible(usingHeadset); + wiredHeadsetItem.setEnabled(usingHeadset); + // TODO: Show the above item (either earpieceItem or wiredHeadsetItem) + // as initially "selected" if speakerOn and + // bluetoothIndicatorOn are both false. + + final MenuItem bluetoothItem = menu.findItem(R.id.audio_mode_bluetooth); + bluetoothItem.setEnabled(isSupported(AudioMode.BLUETOOTH)); + // TODO: Show bluetoothItem as initially "selected" if + // bluetoothIndicatorOn is true. + + mAudioModePopup.show(); + + // Unfortunately we need to manually keep track of the popup menu's + // visiblity, since PopupMenu doesn't have an isShowing() method like + // Dialogs do. + mAudioModePopupVisible = true; + } + + private boolean isSupported(int mode) { + return (mode == (getPresenter().getSupportedAudio() & mode)); + } + + private boolean isAudio(int mode) { + return (mode == getPresenter().getAudioMode()); } @Override |