FileDocCategorySizeDatePackage
InCallMenu.javaAPI DocAndroid 1.5 API16630Wed May 06 22:42:46 BST 2009com.android.phone

InCallMenu

public class InCallMenu extends Object
Helper class to manage the options menu for the InCallScreen. This class is the "Model" (in M-V-C nomenclature) for the in-call menu; it knows about all possible menu items, and contains logic to determine the current state and enabledness of each item based on the state of the Phone. The corresponding View classes are InCallMenuView, which is used purely to lay out and draw the menu, and InCallMenuItemView, which is the View for a single item.

Fields Summary
private static final String
LOG_TAG
private static final boolean
DBG
private InCallScreen
mInCallScreen
Reference to the InCallScreen activity that owns us. This will be null if we haven't been initialized yet *or* after the InCallScreen activity has been destroyed.
private InCallMenuView
mInCallMenuView
Our corresponding View class.
InCallMenuItemView
mManageConference
All possible menu items (see initMenu().)
InCallMenuItemView
mShowDialpad
InCallMenuItemView
mEndCall
InCallMenuItemView
mAddCall
InCallMenuItemView
mSwapCalls
InCallMenuItemView
mMergeCalls
InCallMenuItemView
mBluetooth
InCallMenuItemView
mSpeaker
InCallMenuItemView
mMute
InCallMenuItemView
mHold
InCallMenuItemView
mAnswerAndHold
InCallMenuItemView
mAnswerAndEnd
Constructors Summary
InCallMenu(InCallScreen inCallScreen)


      
        if (DBG) log("InCallMenu constructor...");
        mInCallScreen = inCallScreen;
    
Methods Summary
voidclearInCallScreenReference()
Null out our reference to the InCallScreen activity. This indicates that the InCallScreen activity has been destroyed.

        mInCallScreen = null;
        if (mInCallMenuView != null) mInCallMenuView.clearInCallScreenReference();
    
InCallMenuViewgetView()

        return mInCallMenuView;
    
voidinitMenu()
Initializes the in-call menu by creating a new InCallMenuView, creating all possible menu items, and loading them into the InCallMenuView. The only initialization of the individual items we do here is one-time stuff, like setting the ID and click listener, or calling setIndicatorVisible() for buttons that have a green LED, or calling setText() for buttons whose text never changes. The actual *current* state and enabledness of each item is set in updateItems().

        if (DBG) log("initMenu()...");

        // Explicitly use the "icon menu" theme for the Views we create.
        Context wrappedContext = new ContextThemeWrapper(
                mInCallScreen,
                com.android.internal.R.style.Theme_IconMenu);

        mInCallMenuView = new InCallMenuView(wrappedContext, mInCallScreen);

        //
        // Create all possible InCallMenuView objects.
        //

        mManageConference = new InCallMenuItemView(wrappedContext);
        mManageConference.setId(R.id.menuManageConference);
        mManageConference.setOnClickListener(mInCallScreen);
        mManageConference.setText(R.string.menu_manageConference);
        mManageConference.setIconResource(com.android.internal.R.drawable.ic_menu_allfriends);

        mShowDialpad = new InCallMenuItemView(wrappedContext);
        mShowDialpad.setId(R.id.menuShowDialpad);
        mShowDialpad.setOnClickListener(mInCallScreen);
        mShowDialpad.setText(R.string.menu_showDialpad); // or "Hide dialpad" if it's open
        mShowDialpad.setIconResource(R.drawable.ic_menu_dial_pad);

        mEndCall = new InCallMenuItemView(wrappedContext);
        mEndCall.setId(R.id.menuEndCall);
        mEndCall.setOnClickListener(mInCallScreen);
        mEndCall.setText(R.string.menu_endCall);
        mEndCall.setIconResource(R.drawable.ic_menu_end_call);

        mAddCall = new InCallMenuItemView(wrappedContext);
        mAddCall.setId(R.id.menuAddCall);
        mAddCall.setOnClickListener(mInCallScreen);
        mAddCall.setText(R.string.menu_addCall);
        mAddCall.setIconResource(android.R.drawable.ic_menu_add);

        mSwapCalls = new InCallMenuItemView(wrappedContext);
        mSwapCalls.setId(R.id.menuSwapCalls);
        mSwapCalls.setOnClickListener(mInCallScreen);
        mSwapCalls.setText(R.string.menu_swapCalls);
        mSwapCalls.setIconResource(R.drawable.ic_menu_swap_calls);

        mMergeCalls = new InCallMenuItemView(wrappedContext);
        mMergeCalls.setId(R.id.menuMergeCalls);
        mMergeCalls.setOnClickListener(mInCallScreen);
        mMergeCalls.setText(R.string.menu_mergeCalls);
        mMergeCalls.setIconResource(R.drawable.ic_menu_merge_calls);

        // TODO: Icons for menu items we don't have yet:
        //   R.drawable.ic_menu_answer_call
        //   R.drawable.ic_menu_silence_ringer

        mBluetooth = new InCallMenuItemView(wrappedContext);
        mBluetooth.setId(R.id.menuBluetooth);
        mBluetooth.setOnClickListener(mInCallScreen);
        mBluetooth.setText(R.string.menu_bluetooth);
        mBluetooth.setIndicatorVisible(true);

        mSpeaker = new InCallMenuItemView(wrappedContext);
        mSpeaker.setId(R.id.menuSpeaker);
        mSpeaker.setOnClickListener(mInCallScreen);
        mSpeaker.setText(R.string.menu_speaker);
        mSpeaker.setIndicatorVisible(true);

        mMute = new InCallMenuItemView(wrappedContext);
        mMute.setId(R.id.menuMute);
        mMute.setOnClickListener(mInCallScreen);
        mMute.setText(R.string.menu_mute);
        mMute.setIndicatorVisible(true);

        mHold = new InCallMenuItemView(wrappedContext);
        mHold.setId(R.id.menuHold);
        mHold.setOnClickListener(mInCallScreen);
        mHold.setText(R.string.menu_hold);
        mHold.setIndicatorVisible(true);

        mAnswerAndHold = new InCallMenuItemView(wrappedContext);
        mAnswerAndHold.setId(R.id.menuAnswerAndHold);
        mAnswerAndHold.setOnClickListener(mInCallScreen);
        mAnswerAndHold.setText(R.string.menu_answerAndHold);

        mAnswerAndEnd = new InCallMenuItemView(wrappedContext);
        mAnswerAndEnd.setId(R.id.menuAnswerAndEnd);
        mAnswerAndEnd.setOnClickListener(mInCallScreen);
        mAnswerAndEnd.setText(R.string.menu_answerAndEnd);

        //
        // Load all the items into the correct "slots" in the InCallMenuView.
        //
        // Row 0 is the topmost row onscreen, item 0 is the leftmost item in a row.
        //
        // Individual items may be disabled or hidden, but never move between
        // rows or change their order within a row.
        //
        // TODO: these items and their layout ought be specifiable
        // entirely in XML (just like we currently do with res/menu/*.xml
        // files.)
        //

        // Row 0:
        // This usually has "Show/Hide dialpad", but that gets replaced by
        // "Manage conference" if a conference call is active.
        mInCallMenuView.addItemView(mManageConference, 0);
        mInCallMenuView.addItemView(mShowDialpad, 0);

        // Row 1:
        mInCallMenuView.addItemView(mSwapCalls, 1);
        mInCallMenuView.addItemView(mMergeCalls, 1);
        mInCallMenuView.addItemView(mAddCall, 1);
        mInCallMenuView.addItemView(mEndCall, 1);

        // Row 2:
        // In this row we see *either*  bluetooth/speaker/mute/hold
        // *or* answerAndHold/answerAndEnd, but never all 6 together.
        mInCallMenuView.addItemView(mHold, 2);
        mInCallMenuView.addItemView(mMute, 2);
        mInCallMenuView.addItemView(mSpeaker, 2);
        mInCallMenuView.addItemView(mBluetooth, 2);
        mInCallMenuView.addItemView(mAnswerAndHold, 2);
        mInCallMenuView.addItemView(mAnswerAndEnd, 2);

        mInCallMenuView.dumpState();
    
private voidlog(java.lang.String msg)

        Log.d(LOG_TAG, msg);
    
private voidupdateBluetoothButton()

        mBluetooth.setVisible(true);
        if (mInCallScreen.isBluetoothAvailable()) {
            mBluetooth.setEnabled(true);
            boolean audioConnectedOrPending = mInCallScreen.isBluetoothAudioConnectedOrPending();
            mBluetooth.setIndicatorState(audioConnectedOrPending);
        } else {
            mBluetooth.setEnabled(false);
            mBluetooth.setIndicatorState(false);
        }
    
booleanupdateItems(com.android.internal.telephony.Phone phone)
Updates the enabledness and visibility of all items in the InCallMenuView based on the current state of the Phone. This is called every time we need to display the menu, right before it becomes visible.

return
true if we successfully updated the items and it's OK to go ahead and show the menu, or false if we shouldn't show the menu at all.

        if (DBG) log("updateItems()...");
        // if (DBG) PhoneUtils.dumpCallState(phone);

        // If the phone is totally idle (like in the "call ended" state)
        // there's no menu at all.
        if (phone.getState() == Phone.State.IDLE) {
            if (DBG) log("- Phone is idle!  Don't show the menu...");
            return false;
        }

        final boolean hasRingingCall = !phone.getRingingCall().isIdle();
        final boolean hasActiveCall = !phone.getForegroundCall().isIdle();
        final Call.State fgCallState = phone.getForegroundCall().getState();
        final boolean hasHoldingCall = !phone.getBackgroundCall().isIdle();

        // Special cases when an incoming call is ringing.
        if (hasRingingCall) {
            // In the "call waiting" state, show ONLY the "answer & end"
            // and "answer & hold" buttons, and nothing else.
            // TODO: be sure to test this for "only one line in use and it's
            // active" AND for "only one line in use and it's on hold".
            if (hasActiveCall && !hasHoldingCall) {
                mAnswerAndHold.setVisible(true);
                mAnswerAndHold.setEnabled(true);
                mAnswerAndEnd.setVisible(true);
                mAnswerAndEnd.setEnabled(true);

                mManageConference.setVisible(false);
                mShowDialpad.setVisible(false);
                mEndCall.setVisible(false);
                mAddCall.setVisible(false);
                mSwapCalls.setVisible(false);
                mMergeCalls.setVisible(false);
                mBluetooth.setVisible(false);
                mSpeaker.setVisible(false);
                mMute.setVisible(false);
                mHold.setVisible(false);

                // Done updating the individual items.
                // The last step is to tell the InCallMenuView to update itself
                // based on any visibility changes that just happened.
                mInCallMenuView.updateVisibility();

                return true;
            } else {
                // If there's an incoming ringing call but there aren't
                // any "special actions" to take, don't show a menu at all.
                return false;
            }
        }

        // TODO: double-check if any items here need to be disabled based on:
        //   boolean keyguardRestricted = mInCallScreen.isPhoneStateRestricted();

        // Manage conference: visible only if the foreground call is a
        // conference call.  Enabled unless the "Manage conference" UI is
        // already up.
        boolean canManageConference =
                PhoneUtils.isConferenceCall(phone.getForegroundCall());
        mManageConference.setVisible(canManageConference);
        mManageConference.setEnabled(!mInCallScreen.isManageConferenceMode());

        // "Show/Hide dialpad":
        // - Visible: only in portrait mode, but NOT when "Manage
        //   conference" is available (since that's shown instead.)
        // - Enabled: Only when it's OK to use the dialpad in the first
        //   place (i.e. in the same states where the SlidingDrawer handle
        //   is visible.)
        // - Text label: "Show" or "Hide", depending on the current state
        //   of the sliding drawer.
        boolean showShowDialpad = !InCallScreen.ConfigurationHelper.isLandscape()
                && !canManageConference;
        boolean enableShowDialpad = showShowDialpad && mInCallScreen.okToShowDialpad();
        mShowDialpad.setVisible(showShowDialpad);
        mShowDialpad.setEnabled(enableShowDialpad);
        mShowDialpad.setText(mInCallScreen.isDialerOpened()
                             ? R.string.menu_hideDialpad
                             : R.string.menu_showDialpad);

        // "End call": this button has no state and is always visible.
        // It's also always enabled.  (Actually it *would* need to be
        // disabled if the phone was totally idle, but the entire in-call
        // menu is already disabled in that case (see above.))
        mEndCall.setVisible(true);
        mEndCall.setEnabled(true);

        // "Add call"
        mAddCall.setVisible(true);
        mAddCall.setEnabled(PhoneUtils.okToAddCall(phone));

        // Swap / merge calls
        boolean canSwap = PhoneUtils.okToSwapCalls(phone);
        boolean canMerge = PhoneUtils.okToMergeCalls(phone);
        mSwapCalls.setVisible(true);
        mSwapCalls.setEnabled(canSwap);
        mMergeCalls.setVisible(true);
        mMergeCalls.setEnabled(canMerge);

        // "Bluetooth": always visible, only enabled if BT is available.
        updateBluetoothButton();

        // "Speaker": always visible.  Disabled if a wired headset is
        // plugged in, otherwise enabled (and indicates the current
        // speaker state.)
        mSpeaker.setVisible(true);
        if (PhoneApp.getInstance().isHeadsetPlugged()) {
            // Wired headset is present; Speaker button is meaningless.
            mSpeaker.setEnabled(false);
            mSpeaker.setIndicatorState(false);
        } else {
            // No wired headset; Speaker button is enabled and behaves normally.
            mSpeaker.setEnabled(true);
            boolean speakerOn = PhoneUtils.isSpeakerOn(mInCallScreen.getApplicationContext());
            mSpeaker.setIndicatorState(speakerOn);
        }

        // "Mute": only enabled when the foreground call is ACTIVE.
        // (It's meaningless while on hold, or while DIALING/ALERTING.)
        mMute.setVisible(true);
        boolean muteOn = PhoneUtils.getMute(phone);
        boolean canMute = (fgCallState == Call.State.ACTIVE);
        mMute.setIndicatorState(muteOn);
        mMute.setEnabled(canMute);

        // "Hold": "On hold" means that there's a holding call and
        // *no* foreground call.  (If there *is* a foreground call,
        // that's "two lines in use".)  "Hold" is disabled if both
        // lines are in use, or if the foreground call is non-idle and
        // in any state other than ACTIVE.
        mHold.setVisible(true);
        boolean onHold = hasHoldingCall && !hasActiveCall;
        boolean canHold = !((hasActiveCall && hasHoldingCall)
                            || (hasActiveCall && (fgCallState != Call.State.ACTIVE)));
        mHold.setIndicatorState(onHold);
        mHold.setEnabled(canHold);

        // "Answer & end" and "Answer & hold" are only useful
        // when there's an incoming ringing call (see above.)
        mAnswerAndHold.setVisible(false);
        mAnswerAndHold.setEnabled(false);
        mAnswerAndEnd.setVisible(false);
        mAnswerAndEnd.setEnabled(false);

        // Done updating the individual items.
        // The last step is to tell the InCallMenuView to update itself
        // based on any visibility changes that just happened.
        mInCallMenuView.updateVisibility();

        return true;