Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.phone;
     18 
     19 import android.os.SystemProperties;
     20 import android.text.TextUtils;
     21 import android.util.Log;
     22 import android.view.View;
     23 import android.view.ViewGroup;
     24 import android.view.ViewStub;
     25 import android.widget.Button;
     26 import android.widget.Chronometer;
     27 import android.widget.ImageButton;
     28 import android.widget.TextView;
     29 
     30 import com.android.internal.telephony.CallerInfo;
     31 import com.android.internal.telephony.CallerInfoAsyncQuery;
     32 import com.android.internal.telephony.CallManager;
     33 import com.android.internal.telephony.Connection;
     34 
     35 import java.util.List;
     36 
     37 
     38 /**
     39  * Helper class to initialize and run the InCallScreen's "Manage conference" UI.
     40  */
     41 public class ManageConferenceUtils {
     42     private static final String LOG_TAG = "ManageConferenceUtils";
     43     private static final boolean DBG =
     44             (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
     45 
     46     /**
     47      * CallerInfoAsyncQuery.OnQueryCompleteListener implementation.
     48      *
     49      * This object listens for results from the caller-id info queries we
     50      * fire off in updateManageConferenceRow(), and updates the
     51      * corresponding conference row.
     52      */
     53     private final class QueryCompleteListener
     54             implements CallerInfoAsyncQuery.OnQueryCompleteListener {
     55         private final int mConferencCallListIndex;
     56 
     57         public QueryCompleteListener(int index) {
     58             mConferencCallListIndex = index;
     59         }
     60 
     61         @Override
     62         public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
     63             if (DBG) log("callerinfo query complete, updating UI." + ci);
     64 
     65             Connection connection = (Connection) cookie;
     66             int presentation = connection.getNumberPresentation();
     67 
     68             // get the viewgroup (conference call list item) and make it visible
     69             ViewGroup viewGroup = mConferenceCallList[mConferencCallListIndex];
     70             viewGroup.setVisibility(View.VISIBLE);
     71 
     72             // update the list item with this information.
     73             displayCallerInfoForConferenceRow(ci, presentation,
     74                     (TextView) viewGroup.findViewById(R.id.conferenceCallerName),
     75                     (TextView) viewGroup.findViewById(R.id.conferenceCallerNumberType),
     76                     (TextView) viewGroup.findViewById(R.id.conferenceCallerNumber));
     77         }
     78     }
     79 
     80     private InCallScreen mInCallScreen;
     81     private CallManager mCM;
     82 
     83     // "Manage conference" UI elements and state
     84     private ViewGroup mManageConferencePanel;
     85     private View mButtonManageConferenceDone;
     86     private ViewGroup[] mConferenceCallList;
     87     private int mNumCallersInConference;
     88     private Chronometer mConferenceTime;
     89 
     90     // See CallTracker.MAX_CONNECTIONS_PER_CALL
     91     private static final int MAX_CALLERS_IN_CONFERENCE = 5;
     92 
     93     public ManageConferenceUtils(InCallScreen inCallScreen, CallManager cm) {
     94         if (DBG) log("ManageConferenceUtils constructor...");
     95         mInCallScreen = inCallScreen;
     96         mCM = cm;
     97     }
     98 
     99     public void initManageConferencePanel() {
    100         if (DBG) log("initManageConferencePanel()...");
    101         if (mManageConferencePanel == null) {
    102             if (DBG) log("initManageConferencePanel: first-time initialization!");
    103 
    104             // Inflate the ViewStub, look up and initialize the UI elements.
    105             ViewStub stub = (ViewStub) mInCallScreen.findViewById(R.id.manageConferencePanelStub);
    106             stub.inflate();
    107 
    108             mManageConferencePanel =
    109                     (ViewGroup) mInCallScreen.findViewById(R.id.manageConferencePanel);
    110             if (mManageConferencePanel == null) {
    111                 throw new IllegalStateException("Couldn't find manageConferencePanel!");
    112             }
    113 
    114             // set up the Conference Call chronometer
    115             mConferenceTime =
    116                     (Chronometer) mInCallScreen.findViewById(R.id.manageConferencePanelHeader);
    117             mConferenceTime.setFormat(mInCallScreen.getString(R.string.caller_manage_header));
    118 
    119             // Create list of conference call widgets
    120             mConferenceCallList = new ViewGroup[MAX_CALLERS_IN_CONFERENCE];
    121 
    122             final int[] viewGroupIdList = { R.id.caller0, R.id.caller1, R.id.caller2,
    123                                             R.id.caller3, R.id.caller4 };
    124             for (int i = 0; i < MAX_CALLERS_IN_CONFERENCE; i++) {
    125                 mConferenceCallList[i] =
    126                         (ViewGroup) mInCallScreen.findViewById(viewGroupIdList[i]);
    127             }
    128 
    129             mButtonManageConferenceDone = mInCallScreen.findViewById(R.id.manage_done);
    130             mButtonManageConferenceDone.setOnClickListener(mInCallScreen);
    131         }
    132     }
    133 
    134     /**
    135      * Shows or hides the manageConferencePanel.
    136      */
    137     public void setPanelVisible(boolean visible) {
    138         if (mManageConferencePanel != null) {
    139             mManageConferencePanel.setVisibility(visible ? View.VISIBLE : View.GONE);
    140         }
    141     }
    142 
    143     /**
    144      * Starts the "conference time" chronometer.
    145      */
    146     public void startConferenceTime(long base) {
    147         if (mConferenceTime != null) {
    148             mConferenceTime.setBase(base);
    149             mConferenceTime.start();
    150         }
    151     }
    152 
    153     /**
    154      * Stops the "conference time" chronometer.
    155      */
    156     public void stopConferenceTime() {
    157         if (mConferenceTime != null) {
    158             mConferenceTime.stop();
    159         }
    160     }
    161 
    162     public int getNumCallersInConference() {
    163         return mNumCallersInConference;
    164     }
    165 
    166     /**
    167      * Updates the "Manage conference" UI based on the specified List of
    168      * connections.
    169      *
    170      * @param connections the List of connections belonging to
    171      *        the current foreground call; size must be greater than 1
    172      *        (or it wouldn't be a conference call in the first place.)
    173      */
    174     public void updateManageConferencePanel(List<Connection> connections) {
    175         mNumCallersInConference = connections.size();
    176         if (DBG) log("updateManageConferencePanel()... num connections in conference = "
    177                       + mNumCallersInConference);
    178 
    179         // Can we give the user the option to separate out ("go private with") a single
    180         // caller from this conference?
    181         final boolean hasActiveCall = mCM.hasActiveFgCall();
    182         final boolean hasHoldingCall = mCM.hasActiveBgCall();
    183         boolean canSeparate = !(hasActiveCall && hasHoldingCall);
    184 
    185         for (int i = 0; i < MAX_CALLERS_IN_CONFERENCE; i++) {
    186             if (i < mNumCallersInConference) {
    187                 // Fill in the row in the UI for this caller.
    188                 Connection connection = (Connection) connections.get(i);
    189                 updateManageConferenceRow(i, connection, canSeparate);
    190             } else {
    191                 // Blank out this row in the UI
    192                 updateManageConferenceRow(i, null, false);
    193             }
    194         }
    195     }
    196 
    197     /**
    198      * Updates a single row of the "Manage conference" UI.  (One row in this
    199      * UI represents a single caller in the conference.)
    200      *
    201      * @param i the row to update
    202      * @param connection the Connection corresponding to this caller.
    203      *        If null, that means this is an "empty slot" in the conference,
    204      *        so hide this row in the UI.
    205      * @param canSeparate if true, show a "Separate" (i.e. "Private") button
    206      *        on this row in the UI.
    207      */
    208     public void updateManageConferenceRow(final int i,
    209                                           final Connection connection,
    210                                           boolean canSeparate) {
    211         if (DBG) log("updateManageConferenceRow(" + i + ")...  connection = " + connection);
    212 
    213         if (connection != null) {
    214             // Activate this row of the Manage conference panel:
    215             mConferenceCallList[i].setVisibility(View.VISIBLE);
    216 
    217             // get the relevant children views
    218             View endButton = mConferenceCallList[i].findViewById(R.id.conferenceCallerDisconnect);
    219             View separateButton = mConferenceCallList[i].findViewById(
    220                     R.id.conferenceCallerSeparate);
    221             TextView nameTextView = (TextView) mConferenceCallList[i].findViewById(
    222                     R.id.conferenceCallerName);
    223             TextView numberTextView = (TextView) mConferenceCallList[i].findViewById(
    224                     R.id.conferenceCallerNumber);
    225             TextView numberTypeTextView = (TextView) mConferenceCallList[i].findViewById(
    226                     R.id.conferenceCallerNumberType);
    227 
    228             if (DBG) log("- button: " + endButton + ", nameTextView: " + nameTextView);
    229 
    230             // Hook up this row's buttons.
    231             View.OnClickListener endThisConnection = new View.OnClickListener() {
    232                     @Override
    233                     public void onClick(View v) {
    234                         endConferenceConnection(i, connection);
    235                         PhoneGlobals.getInstance().pokeUserActivity();
    236                     }
    237                 };
    238             endButton.setOnClickListener(endThisConnection);
    239             //
    240             if (canSeparate) {
    241                 View.OnClickListener separateThisConnection = new View.OnClickListener() {
    242                         @Override
    243                         public void onClick(View v) {
    244                             separateConferenceConnection(i, connection);
    245                             PhoneGlobals.getInstance().pokeUserActivity();
    246                         }
    247                     };
    248                 separateButton.setOnClickListener(separateThisConnection);
    249                 separateButton.setVisibility(View.VISIBLE);
    250             } else {
    251                 separateButton.setVisibility(View.INVISIBLE);
    252             }
    253 
    254             // Name/number for this caller.
    255             QueryCompleteListener listener = new QueryCompleteListener(i);
    256             PhoneUtils.CallerInfoToken info =
    257                     PhoneUtils.startGetCallerInfo(mInCallScreen,
    258                             connection, listener, connection);
    259             if (DBG) log("  - got info from startGetCallerInfo(): " + info);
    260 
    261             // display the CallerInfo.
    262             displayCallerInfoForConferenceRow(info.currentInfo, connection.getNumberPresentation(),
    263                     nameTextView, numberTypeTextView, numberTextView);
    264         } else {
    265             // Disable this row of the Manage conference panel:
    266             mConferenceCallList[i].setVisibility(View.GONE);
    267         }
    268     }
    269 
    270     /**
    271      * Helper function to fill out the Conference Call(er) information
    272      * for each item in the "Manage Conference Call" list.
    273      *
    274      * @param presentation presentation specified by {@link Connection}.
    275      */
    276     public final void displayCallerInfoForConferenceRow(CallerInfo ci, int presentation,
    277             TextView nameTextView, TextView numberTypeTextView, TextView numberTextView) {
    278         // gather the correct name and number information.
    279         String callerName = "";
    280         String callerNumber = "";
    281         String callerNumberType = "";
    282         if (ci != null) {
    283             callerName = ci.name;
    284             if (TextUtils.isEmpty(callerName)) {
    285                 // Do similar fallback as CallCard does.
    286                 // See also CallCard#updateDisplayForPerson().
    287                 if (TextUtils.isEmpty(ci.phoneNumber)) {
    288                     callerName = PhoneUtils.getPresentationString(mInCallScreen, presentation);
    289                 } else if (!TextUtils.isEmpty(ci.cnapName)) {
    290                     // No name, but we do have a valid CNAP name, so use that.
    291                     callerName = ci.cnapName;
    292                 } else {
    293                     callerName = ci.phoneNumber;
    294                 }
    295             } else {
    296                 callerNumber = ci.phoneNumber;
    297                 callerNumberType = ci.phoneLabel;
    298             }
    299         }
    300 
    301         // set the caller name
    302         nameTextView.setText(callerName);
    303 
    304         // set the caller number in subscript, or make the field disappear.
    305         if (TextUtils.isEmpty(callerNumber)) {
    306             numberTextView.setVisibility(View.GONE);
    307             numberTypeTextView.setVisibility(View.GONE);
    308         } else {
    309             numberTextView.setVisibility(View.VISIBLE);
    310             numberTextView.setText(callerNumber);
    311             numberTypeTextView.setVisibility(View.VISIBLE);
    312             numberTypeTextView.setText(callerNumberType);
    313         }
    314     }
    315 
    316     /**
    317      * Ends the specified connection on a conference call.  This method is
    318      * run (via a closure containing a row index and Connection) when the
    319      * user clicks the "End" button on a specific row in the Manage
    320      * conference UI.
    321      */
    322     public void endConferenceConnection(int i, Connection connection) {
    323         if (DBG) log("===> ENDING conference connection " + i
    324                       + ": Connection " + connection);
    325         // The actual work of ending the connection:
    326         PhoneUtils.hangup(connection);
    327         // No need to manually update the "Manage conference" UI here;
    328         // that'll happen automatically very soon (when we get the
    329         // onDisconnect() callback triggered by this hangup() call.)
    330     }
    331 
    332     /**
    333      * Separates out the specified connection on a conference call.  This
    334      * method is run (via a closure containing a row index and Connection)
    335      * when the user clicks the "Separate" (i.e. "Private") button on a
    336      * specific row in the Manage conference UI.
    337      */
    338     public void separateConferenceConnection(int i, Connection connection) {
    339         if (DBG) log("===> SEPARATING conference connection " + i
    340                       + ": Connection " + connection);
    341 
    342         PhoneUtils.separateCall(connection);
    343 
    344         // Note that separateCall() automagically makes the
    345         // newly-separated call into the foreground call (which is the
    346         // desired UI), so there's no need to do any further
    347         // call-switching here.
    348         // There's also no need to manually update (or hide) the "Manage
    349         // conference" UI; that'll happen on its own in a moment (when we
    350         // get the phone state change event triggered by the call to
    351         // separateCall().)
    352     }
    353 
    354 
    355     private void log(String msg) {
    356         Log.d(LOG_TAG, msg);
    357     }
    358 }
    359