Merge "Modifications to bold contacts' first names."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 83331da..ed03e77 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -336,6 +336,22 @@
</intent-filter>
</activity>
+
+ <!-- List of groups -->
+ <activity android:name=".activities.GroupBrowserActivity"
+ android:label="@string/contactsGroupsLabel"
+ android:theme="@style/ContactBrowserTheme"
+ android:launchMode="singleTop"
+ android:clearTaskOnLaunch="true">
+ <!-- TODO: Remove this temporary intent action name when the fragmentization
+ work is done. -->
+ <intent-filter>
+ <action android:name="com.android.phone.action.GROUPS_LIST" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.TAB" />
+ </intent-filter>
+ </activity>
+
<!-- Used to show QuickContact window over a translucent activity, which is a
temporary hack until we add better framework support. -->
<activity
diff --git a/res/layout-land/dialpad_activity.xml b/res/layout-land/dialpad_fragment.xml
similarity index 97%
rename from res/layout-land/dialpad_activity.xml
rename to res/layout-land/dialpad_fragment.xml
index 985d047..fe9fb28 100644
--- a/res/layout-land/dialpad_activity.xml
+++ b/res/layout-land/dialpad_fragment.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/res/layout-long-land/dialpad_activity.xml b/res/layout-long-land/dialpad_fragment.xml
similarity index 97%
rename from res/layout-long-land/dialpad_activity.xml
rename to res/layout-long-land/dialpad_fragment.xml
index 1cf9690..f287741 100644
--- a/res/layout-long-land/dialpad_activity.xml
+++ b/res/layout-long-land/dialpad_fragment.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/res/layout-long/dialpad_activity.xml b/res/layout-long/dialpad_fragment.xml
similarity index 97%
rename from res/layout-long/dialpad_activity.xml
rename to res/layout-long/dialpad_fragment.xml
index 2580625..85250e2 100644
--- a/res/layout-long/dialpad_activity.xml
+++ b/res/layout-long/dialpad_fragment.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/res/layout-xlarge/account_selector_list_item.xml b/res/layout-xlarge/account_selector_list_item.xml
new file mode 100644
index 0000000..38acfc5
--- /dev/null
+++ b/res/layout-xlarge/account_selector_list_item.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+>
+ <ImageView android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:layout_centerVertical="true"
+ />
+
+ <TextView android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:layout_marginTop="6dip"
+ android:layout_toRightOf="@android:id/icon"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+ <TextView android:id="@android:id/text2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/text1"
+ android:layout_alignLeft="@android:id/text1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+</RelativeLayout>
diff --git a/res/layout/account_selector_list_item.xml b/res/layout/account_selector_list_item.xml
index 38acfc5..6fe2a50 100644
--- a/res/layout/account_selector_list_item.xml
+++ b/res/layout/account_selector_list_item.xml
@@ -17,29 +17,23 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
->
- <ImageView android:id="@android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="6dip"
- android:layout_centerVertical="true"
- />
+ android:paddingLeft="@dimen/account_selector_horizontal_margin"
+ android:paddingRight="@dimen/account_selector_horizontal_margin"
+ android:minHeight="@dimen/account_selector_min_item_height" >
<TextView android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginLeft="6dip"
- android:layout_marginTop="6dip"
- android:layout_toRightOf="@android:id/icon"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:layout_alignParentLeft="true"
+ android:layout_toLeftOf="@android:id/icon"
+ android:layout_centerVertical="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
/>
- <TextView android:id="@android:id/text2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/text1"
- android:layout_alignLeft="@android:id/text1"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ <ImageView android:id="@android:id/icon"
+ android:layout_width="@dimen/account_selector_icon_size"
+ android:layout_height="@dimen/account_selector_icon_size"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
/>
</RelativeLayout>
diff --git a/res/layout/dialpad_activity.xml b/res/layout/dialpad_activity.xml
index 2cae9ae..93e9523 100644
--- a/res/layout/dialpad_activity.xml
+++ b/res/layout/dialpad_activity.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,52 +14,12 @@
limitations under the License.
-->
-<!-- TODO (stopship) We don't want to specify a background color here. For now we just
-keep it because otherwise the dialer needs some imagination to use (white on white) -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/top"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@android:color/black"
>
-
- <!-- Text field above the keypad where the digits are displayed.
- It's type is set to NULL (to disable the IME keyboard) in the
- java code.
- -->
- <!-- TODO: Use a textAppearance to control the display of the number -->
- <EditText android:id="@+id/digits"
- android:layout_width="match_parent"
- android:layout_height="67dip"
- android:gravity="center"
- android:maxLines="1"
- android:scrollHorizontally="true"
- android:textSize="33sp"
- android:freezesText="true"
- android:background="@drawable/btn_dial_textfield"
- android:textColor="@color/dialer_button_text"
- android:focusableInTouchMode="true"
- android:editable="true"
- android:cursorVisible="false"
- android:layout_weight="0"
- android:contentDescription="@string/description_digits_edittext"
- />
-
- <!-- Keypad section -->
- <include layout="@layout/dialpad" />
-
- <!-- Horizontal row of buttons (Voicemail + DialButton + Delete.) -->
- <include layout="@layout/voicemail_dial_delete" />
-
- <!-- "Dialpad chooser" UI, shown only when the user brings up the
- Dialer while a call is already in progress.
- When this UI is visible, the other Dialer elements
- (the textfield/button and the dialpad) are hidden. -->
- <ListView android:id="@+id/dialpadChooser"
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_weight="1"
- />
-
-</LinearLayout>
+ <fragment class="com.android.contacts.dialpad.DialpadFragment"
+ android:id="@+id/dialpad_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</FrameLayout>
diff --git a/res/layout-long/dialpad_activity.xml b/res/layout/dialpad_fragment.xml
similarity index 91%
copy from res/layout-long/dialpad_activity.xml
copy to res/layout/dialpad_fragment.xml
index 2580625..c516bd8 100644
--- a/res/layout-long/dialpad_activity.xml
+++ b/res/layout/dialpad_fragment.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -31,11 +31,11 @@
<!-- TODO: Use a textAppearance to control the display of the number -->
<EditText android:id="@+id/digits"
android:layout_width="match_parent"
- android:layout_height="74dip"
+ android:layout_height="67dip"
android:gravity="center"
android:maxLines="1"
android:scrollHorizontally="true"
- android:textSize="34sp"
+ android:textSize="33sp"
android:freezesText="true"
android:background="@drawable/btn_dial_textfield"
android:textColor="@color/dialer_button_text"
@@ -43,6 +43,7 @@
android:editable="true"
android:cursorVisible="false"
android:layout_weight="0"
+ android:contentDescription="@string/description_digits_edittext"
/>
<!-- Keypad section -->
diff --git a/res/layout/group_browse_list_fragment.xml b/res/layout/group_browse_list_fragment.xml
new file mode 100644
index 0000000..50c02c8
--- /dev/null
+++ b/res/layout/group_browse_list_fragment.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ListView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:fastScrollEnabled="true"
+ android:scrollbarStyle="outsideOverlay"
+ android:layout_weight="1" />
+
+ <TextView
+ android:id="@+id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="@string/noGroups"
+ android:visibility="gone"/>
+
+</LinearLayout>
diff --git a/res/layout/group_browse_list_item.xml b/res/layout/group_browse_list_item.xml
new file mode 100644
index 0000000..accbedd
--- /dev/null
+++ b/res/layout/group_browse_list_item.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:scaleType="center"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_gravity="center_vertical" />
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingTop="5dip"
+ android:paddingBottom="5dip">
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:ellipsize="end"
+ android:singleLine="true" />
+
+ <TextView
+ android:id="@+id/account"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textStyle="italic" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/group_browser_activity.xml b/res/layout/group_browser_activity.xml
new file mode 100644
index 0000000..f7187d9
--- /dev/null
+++ b/res/layout/group_browser_activity.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <fragment
+ android:id="@+id/list_fragment"
+ class="com.android.contacts.group.GroupBrowseListFragment"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ />
+
+</FrameLayout>
diff --git a/res/layout/raw_contact_editor_view.xml b/res/layout/raw_contact_editor_view.xml
index 7c027eb..5aa1e95 100644
--- a/res/layout/raw_contact_editor_view.xml
+++ b/res/layout/raw_contact_editor_view.xml
@@ -32,7 +32,7 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginRight="4dip"
- android:background="@color/account_selection_background"
+ android:background="@android:drawable/list_selector_background"
android:orientation="horizontal"
android:gravity="left|center_vertical">
@@ -49,10 +49,13 @@
<LinearLayout
android:id="@+id/account"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_width="match_parent"
+ android:paddingLeft="@dimen/account_selector_horizontal_margin"
+ android:paddingRight="@dimen/account_selector_horizontal_margin"
android:background="@color/account_selection_background"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:gravity="left|center_vertical">
<TextView
android:id="@+id/account_type"
android:layout_width="wrap_content"
@@ -78,8 +81,8 @@
<ImageView
android:id="@+id/account_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_width="@dimen/account_selector_icon_size"
+ android:layout_height="@dimen/account_selector_icon_size" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 354bec7..ff7d873 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -26,6 +26,9 @@
<dimen name="aggregation_suggestion_icon_size">40dip</dimen>
<dimen name="account_selector_popup_width">400dip</dimen>
+ <dimen name="account_selector_icon_size">30dip</dimen>
+ <dimen name="account_selector_min_item_height">48dip</dimen>
+ <dimen name="account_selector_horizontal_margin">6dip</dimen>
<dimen name="photo_action_popup_width">400dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b531431..5605d25 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -264,6 +264,9 @@
<!-- The text displayed when the contacts list is empty while displaying all contacts -->
<string name="noContacts">No contacts.</string>
+ <!-- The text displayed when the groups list is empty while displaying all groups [CHAR LIMIT=NONE] -->
+ <string name="noGroups">No groups.</string>
+
<!-- The text displayed when the contacts list is empty while displaying results after searching contacts -->
<string name="noMatchingContacts">No matching contacts found.</string>
@@ -349,6 +352,9 @@
<!-- The description text for the contacts tab. Space is limited for this string, so the shorter the better -->
<string name="contactsIconLabel">Contacts</string>
+ <!-- The description text for the groups tab. Space is limited for this string, so the shorter the better -->
+ <string name="contactsGroupsLabel">Groups</string>
+
<!-- The description text for the favorites tab. Space is limited for this string, so the shorter the better -->
<string name="contactsFavoritesLabel">Favorites</string>
diff --git a/src/com/android/contacts/activities/DialpadActivity.java b/src/com/android/contacts/activities/DialpadActivity.java
index cfbdff2..a30460a 100644
--- a/src/com/android/contacts/activities/DialpadActivity.java
+++ b/src/com/android/contacts/activities/DialpadActivity.java
@@ -17,12 +17,8 @@
package com.android.contacts.activities;
import com.android.contacts.ContactsSearchManager;
-import com.android.contacts.ContactsUtils;
import com.android.contacts.R;
-import com.android.contacts.SpecialCharSequenceMgr;
-import com.android.internal.telephony.ITelephony;
-import com.android.phone.CallLogAsync;
-import com.android.phone.HapticFeedback;
+import com.android.contacts.dialpad.DialpadFragment;
import android.app.Activity;
import android.content.ActivityNotFoundException;
@@ -30,243 +26,48 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.media.ToneGenerator;
-import android.net.Uri;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
-import android.provider.Settings;
-import android.provider.Contacts.People;
-import android.provider.Contacts.Phones;
-import android.provider.Contacts.PhonesColumns;
-import android.provider.Contacts.Intents.Insert;
-import android.telephony.PhoneNumberFormattingTextWatcher;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.text.method.DialerKeyListener;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewGroup;
import android.view.Window;
import android.view.inputmethod.InputMethodManager;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
/**
- * Dialer activity that displays the typical twelve key interface.
+ * Activity that displays a twelve-key phone dialpad.
+ * This is just a simple container around DialpadFragment.
+ * @see DialpadFragment
*/
-@SuppressWarnings("deprecation")
-public class DialpadActivity extends Activity implements View.OnClickListener,
- View.OnLongClickListener, View.OnKeyListener,
- AdapterView.OnItemClickListener, TextWatcher {
- private static final String EMPTY_NUMBER = "";
+public class DialpadActivity extends Activity {
private static final String TAG = "DialpadActivity";
- /** The length of DTMF tones in milliseconds */
- private static final int TONE_LENGTH_MS = 150;
-
- /** The DTMF tone volume relative to other sounds in the stream */
- private static final int TONE_RELATIVE_VOLUME = 80;
-
- /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
- private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_MUSIC;
-
- private EditText mDigits;
- private View mDelete;
- private MenuItem mAddToContactMenuItem;
- private ToneGenerator mToneGenerator;
- private Object mToneGeneratorLock = new Object();
- private Drawable mDigitsBackground;
- private Drawable mDigitsEmptyBackground;
- private View mDialpad;
- private View mVoicemailDialAndDeleteRow;
- private View mVoicemailButton;
- private View mDialButton;
- private ListView mDialpadChooser;
- private DialpadChooserAdapter mDialpadChooserAdapter;
- //Member variables for dialpad options
- private MenuItem m2SecPauseMenuItem;
- private MenuItem mWaitMenuItem;
- private static final int MENU_ADD_CONTACTS = 1;
- private static final int MENU_2S_PAUSE = 2;
- private static final int MENU_WAIT = 3;
-
- // Last number dialed, retrieved asynchronously from the call DB
- // in onCreate. This number is displayed when the user hits the
- // send key and cleared in onPause.
- CallLogAsync mCallLog = new CallLogAsync();
- private String mLastNumberDialed = EMPTY_NUMBER;
-
- // determines if we want to playback local DTMF tones.
- private boolean mDTMFToneEnabled;
-
- // Vibration (haptic feedback) for dialer key presses.
- private HapticFeedback mHaptic = new HapticFeedback();
-
- /** Identifier for the "Add Call" intent extra. */
- static final String ADD_CALL_MODE_KEY = "add_call_mode";
-
- /**
- * Identifier for intent extra for sending an empty Flash message for
- * CDMA networks. This message is used by the network to simulate a
- * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
- *
- * TODO: Using an intent extra to tell the phone to send this flash is a
- * temporary measure. To be replaced with an ITelephony call in the future.
- * TODO: Keep in sync with the string defined in OutgoingCallBroadcaster.java
- * in Phone app until this is replaced with the ITelephony API.
- */
- static final String EXTRA_SEND_EMPTY_FLASH
- = "com.android.phone.extra.SEND_EMPTY_FLASH";
-
- /** Indicates if we are opening this dialer to add a call from the InCallScreen. */
- private boolean mIsAddCallMode;
-
- private String mCurrentCountryIso;
-
- PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- /**
- * Listen for phone state changes so that we can take down the
- * "dialpad chooser" if the phone becomes idle while the
- * chooser UI is visible.
- */
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
- // + state + ", '" + incomingNumber + "'");
- if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
- // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
- // Note there's a race condition in the UI here: the
- // dialpad chooser could conceivably disappear (on its
- // own) at the exact moment the user was trying to select
- // one of the choices, which would be confusing. (But at
- // least that's better than leaving the dialpad chooser
- // onscreen, but useless...)
- showDialpadChooser(false);
- }
- }
- };
-
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- // Do nothing
- }
-
- public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
- // Do nothing
- // DTMF Tones do not need to be played here any longer -
- // the DTMF dialer handles that functionality now.
- }
-
- public void afterTextChanged(Editable input) {
- if (SpecialCharSequenceMgr.handleChars(this, input.toString(), mDigits)) {
- // A special sequence was entered, clear the digits
- mDigits.getText().clear();
- }
-
- if (!isDigitsEmpty()) {
- mDigits.setBackgroundDrawable(mDigitsBackground);
- } else {
- mDigits.setCursorVisible(false);
- mDigits.setBackgroundDrawable(mDigitsEmptyBackground);
- }
-
- updateDialAndDeleteButtonEnabledState();
- }
+ private DialpadFragment mFragment;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- mCurrentCountryIso = ContactsUtils.getCurrentCountryIso(this);
+ setContentView(R.layout.dialpad_activity);
+
Resources r = getResources();
// Do not show title in the case the device is in carmode.
if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) ==
Configuration.UI_MODE_TYPE_CAR) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
- // Set the content view
- setContentView(getContentViewResource());
- // Load up the resources for the text field.
- mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
- mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
+ mFragment = (DialpadFragment) getFragmentManager().findFragmentById(
+ R.id.dialpad_fragment);
- mDigits = (EditText) findViewById(R.id.digits);
- mDigits.setKeyListener(DialerKeyListener.getInstance());
- mDigits.setOnClickListener(this);
- mDigits.setOnKeyListener(this);
-
- maybeAddNumberFormatting();
-
- // Check for the presence of the keypad
- View view = findViewById(R.id.one);
- if (view != null) {
- setupKeypad();
- }
-
- mVoicemailDialAndDeleteRow = findViewById(R.id.voicemailAndDialAndDelete);
-
- initVoicemailButton();
-
- // Check whether we should show the onscreen "Dial" button.
- mDialButton = mVoicemailDialAndDeleteRow.findViewById(R.id.dialButton);
-
- if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
- mDialButton.setOnClickListener(this);
- } else {
- mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
- mDialButton = null;
- }
-
- view = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
- view.setOnClickListener(this);
- view.setOnLongClickListener(this);
- mDelete = view;
-
- mDialpad = findViewById(R.id.dialpad); // This is null in landscape mode.
-
- // In landscape we put the keyboard in phone mode.
- // In portrait we prevent the soft keyboard to show since the
- // dialpad acts as one already.
- if (null == mDialpad) {
- mDigits.setInputType(android.text.InputType.TYPE_CLASS_PHONE);
- } else {
- mDigits.setInputType(android.text.InputType.TYPE_NULL);
- }
-
- // Set up the "dialpad chooser" UI; see showDialpadChooser().
- mDialpadChooser = (ListView) findViewById(R.id.dialpadChooser);
- mDialpadChooser.setOnItemClickListener(this);
-
- if (!resolveIntent() && icicle != null) {
+ // Manually run the onRestoreInstanceState() sequence here, but only if
+ // our intent does *not* have the DialtactsActivity.EXTRA_IGNORE_STATE
+ // set (see the references to EXTRA_IGNORE_STATE in DialtactsActivity).
+ // TODO: Find a cleaner way of doing this.
+ if (!mFragment.resolveIntent() && (icicle != null)) {
super.onRestoreInstanceState(icicle);
}
-
- try {
- mHaptic.init(this, r.getBoolean(R.bool.config_enable_dialer_key_vibration));
- } catch (Resources.NotFoundException nfe) {
- Log.e(TAG, "Vibrate control bool missing.", nfe);
- }
-
}
@Override
@@ -274,222 +75,22 @@
// Do nothing, state is restored in onCreate() if needed
}
- protected void maybeAddNumberFormatting() {
- mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher(mCurrentCountryIso));
- }
-
- /**
- * Overridden by subclasses to control the resource used by the content view.
- */
- protected int getContentViewResource() {
- return R.layout.dialpad_activity;
- }
-
- private boolean resolveIntent() {
- boolean ignoreState = false;
-
- // Find the proper intent
- final Intent intent;
- if (isChild()) {
- intent = getParent().getIntent();
- ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
- } else {
- intent = getIntent();
- }
- // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
-
- // by default we are not adding a call.
- mIsAddCallMode = false;
-
- // By default we don't show the "dialpad chooser" UI.
- boolean needToShowDialpadChooser = false;
-
- // Resolve the intent
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
- // see if we are "adding a call" from the InCallScreen; false by default.
- mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
-
- Uri uri = intent.getData();
- if (uri != null) {
- if ("tel".equals(uri.getScheme())) {
- // Put the requested number into the input area
- String data = uri.getSchemeSpecificPart();
- setFormattedDigits(data, null);
- } else {
- String type = intent.getType();
- if (People.CONTENT_ITEM_TYPE.equals(type)
- || Phones.CONTENT_ITEM_TYPE.equals(type)) {
- // Query the phone number
- Cursor c = getContentResolver().query(intent.getData(),
- new String[] {PhonesColumns.NUMBER, PhonesColumns.NUMBER_KEY},
- null, null, null);
- if (c != null) {
- if (c.moveToFirst()) {
- // Put the number into the input area
- setFormattedDigits(c.getString(0), c.getString(1));
- }
- c.close();
- }
- }
- }
- } else {
- // ACTION_DIAL or ACTION_VIEW with no data.
- // This behaves basically like ACTION_MAIN: If there's
- // already an active call, bring up an intermediate UI to
- // make the user confirm what they really want to do.
- // Be sure *not* to show the dialpad chooser if this is an
- // explicit "Add call" action, though.
- if (!mIsAddCallMode && phoneIsInUse()) {
- needToShowDialpadChooser = true;
- }
- }
- } else if (Intent.ACTION_MAIN.equals(action)) {
- // The MAIN action means we're bringing up a blank dialer
- // (e.g. by selecting the Home shortcut, or tabbing over from
- // Contacts or Call log.)
- //
- // At this point, IF there's already an active call, there's a
- // good chance that the user got here accidentally (but really
- // wanted the in-call dialpad instead). So we bring up an
- // intermediate UI to make the user confirm what they really
- // want to do.
- if (phoneIsInUse()) {
- // Log.i(TAG, "resolveIntent(): phone is in use; showing dialpad chooser!");
- needToShowDialpadChooser = true;
- }
- }
-
- // Bring up the "dialpad chooser" IFF we need to make the user
- // confirm which dialpad they really want.
- showDialpadChooser(needToShowDialpadChooser);
-
- return ignoreState;
- }
-
- protected void setFormattedDigits(String data, String normalizedNumber) {
- // strip the non-dialable numbers out of the data string.
- String dialString = PhoneNumberUtils.extractNetworkPortion(data);
- dialString =
- PhoneNumberUtils.formatNumber(dialString, normalizedNumber, mCurrentCountryIso);
- if (!TextUtils.isEmpty(dialString)) {
- Editable digits = mDigits.getText();
- digits.replace(0, digits.length(), dialString);
- // for some reason this isn't getting called in the digits.replace call above..
- // but in any case, this will make sure the background drawable looks right
- afterTextChanged(digits);
- }
- }
-
@Override
protected void onNewIntent(Intent newIntent) {
setIntent(newIntent);
- resolveIntent();
+ mFragment.resolveIntent();
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
- // This can't be done in onCreate(), since the auto-restoring of the digits
- // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
- // is called. This method will be called every time the activity is created, and
- // will always happen after onRestoreSavedInstanceState().
- mDigits.addTextChangedListener(this);
+ // Pass this lifecycle event down to the fragment
+ mFragment.onPostCreate();
}
- private void setupKeypad() {
- // Setup the listeners for the buttons
- View view = findViewById(R.id.one);
- view.setOnClickListener(this);
- view.setOnLongClickListener(this);
-
- findViewById(R.id.two).setOnClickListener(this);
- findViewById(R.id.three).setOnClickListener(this);
- findViewById(R.id.four).setOnClickListener(this);
- findViewById(R.id.five).setOnClickListener(this);
- findViewById(R.id.six).setOnClickListener(this);
- findViewById(R.id.seven).setOnClickListener(this);
- findViewById(R.id.eight).setOnClickListener(this);
- findViewById(R.id.nine).setOnClickListener(this);
- findViewById(R.id.star).setOnClickListener(this);
-
- view = findViewById(R.id.zero);
- view.setOnClickListener(this);
- view.setOnLongClickListener(this);
-
- findViewById(R.id.pound).setOnClickListener(this);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- // Query the last dialed number. Do it first because hitting
- // the DB is 'slow'. This call is asynchronous.
- queryLastOutgoingCall();
-
- // retrieve the DTMF tone play back setting.
- mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
- Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
-
- // Retrieve the haptic feedback setting.
- mHaptic.checkSystemSetting();
-
- // if the mToneGenerator creation fails, just continue without it. It is
- // a local audio signal, and is not as important as the dtmf tone itself.
- synchronized(mToneGeneratorLock) {
- if (mToneGenerator == null) {
- try {
- // we want the user to be able to control the volume of the dial tones
- // outside of a call, so we use the stream type that is also mapped to the
- // volume control keys for this activity
- mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
- setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
- } catch (RuntimeException e) {
- Log.w(TAG, "Exception caught while creating local tone generator: " + e);
- mToneGenerator = null;
- }
- }
- }
-
- Activity parent = getParent();
- // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
- // digits in the dialer field.
- if (parent != null && parent instanceof DialtactsActivity) {
- Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
- if (dialUri != null) {
- resolveIntent();
- }
- }
-
- // While we're in the foreground, listen for phone state changes,
- // purely so that we can take down the "dialpad chooser" if the
- // phone becomes idle while the chooser UI is visible.
- TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
- telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
-
- // Potentially show hint text in the mDigits field when the user
- // hasn't typed any digits yet. (If there's already an active call,
- // this hint text will remind the user that he's about to add a new
- // call.)
- //
- // TODO: consider adding better UI for the case where *both* lines
- // are currently in use. (Right now we let the user try to add
- // another call, but that call is guaranteed to fail. Perhaps the
- // entire dialer UI should be disabled instead.)
- if (phoneIsInUse()) {
- mDigits.setHint(R.string.dialerDialpadHintText);
- } else {
- // Common case; no hint necessary.
- mDigits.setHint(null);
-
- // Also, a sanity-check: the "dialpad chooser" UI should NEVER
- // be visible if the phone is idle!
- showDialpadChooser(false);
- }
-
- updateDialAndDeleteButtonEnabledState();
+ public DialpadFragment getFragment() {
+ return mFragment;
}
@Override
@@ -501,96 +102,33 @@
// have a window token yet in onCreate / onNewIntent
InputMethodManager inputMethodManager = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
- inputMethodManager.hideSoftInputFromWindow(mDigits.getWindowToken(), 0);
+ inputMethodManager.hideSoftInputFromWindow(
+ mFragment.getDigitsWidget().getWindowToken(), 0);
}
}
@Override
- protected void onPause() {
- super.onPause();
-
- // Stop listening for phone state changes.
- TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
- telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
-
- synchronized(mToneGeneratorLock) {
- if (mToneGenerator != null) {
- mToneGenerator.release();
- mToneGenerator = null;
- }
- }
- // TODO: I wonder if we should not check if the AsyncTask that
- // lookup the last dialed number has completed.
- mLastNumberDialed = EMPTY_NUMBER; // Since we are going to query again, free stale number.
- }
-
- @Override
public boolean onCreateOptionsMenu(Menu menu) {
- mAddToContactMenuItem = menu.add(0, MENU_ADD_CONTACTS, 0, R.string.recentCalls_addToContact)
- .setIcon(android.R.drawable.ic_menu_add);
- m2SecPauseMenuItem = menu.add(0, MENU_2S_PAUSE, 0, R.string.add_2sec_pause)
- .setIcon(R.drawable.ic_menu_2sec_pause);
- mWaitMenuItem = menu.add(0, MENU_WAIT, 0, R.string.add_wait)
- .setIcon(R.drawable.ic_menu_wait);
+ super.onCreateOptionsMenu(menu);
+
+ // Nothing to do here; see DialpadFragment.onCreateOptionsMenu().
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- // We never show a menu if the "choose dialpad" UI is up.
- if (dialpadChooserVisible()) {
+ // The DialpadFragment completely owns the options menu,
+ // so we don't add any items here. We *do* however
+ // have to return false here if the DialpadFragment
+ // says there shouldn't be a menu at all.
+ if (!mFragment.allowOptionsMenu()) {
return false;
}
- if (isDigitsEmpty()) {
- mAddToContactMenuItem.setVisible(false);
- m2SecPauseMenuItem.setVisible(false);
- mWaitMenuItem.setVisible(false);
- } else {
- CharSequence digits = mDigits.getText();
+ super.onPrepareOptionsMenu(menu);
- // Put the current digits string into an intent
- Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- intent.putExtra(Insert.PHONE, digits);
- intent.setType(People.CONTENT_ITEM_TYPE);
- mAddToContactMenuItem.setIntent(intent);
- mAddToContactMenuItem.setVisible(true);
-
- // Check out whether to show Pause & Wait option menu items
- int selectionStart;
- int selectionEnd;
- String strDigits = digits.toString();
-
- selectionStart = mDigits.getSelectionStart();
- selectionEnd = mDigits.getSelectionEnd();
-
- if (selectionStart != -1) {
- if (selectionStart > selectionEnd) {
- // swap it as we want start to be less then end
- int tmp = selectionStart;
- selectionStart = selectionEnd;
- selectionEnd = tmp;
- }
-
- if (selectionStart != 0) {
- // Pause can be visible if cursor is not in the begining
- m2SecPauseMenuItem.setVisible(true);
-
- // For Wait to be visible set of condition to meet
- mWaitMenuItem.setVisible(showWait(selectionStart,
- selectionEnd, strDigits));
- } else {
- // cursor in the beginning both pause and wait to be invisible
- m2SecPauseMenuItem.setVisible(false);
- mWaitMenuItem.setVisible(false);
- }
- } else {
- // cursor is not selected so assume new digit is added to the end
- int strLength = strDigits.length();
- mWaitMenuItem.setVisible(showWait(strLength,
- strLength, strDigits));
- }
- }
+ // See DialpadFragment.onPrepareOptionsMenu() for the actual menu
+ // contents.
return true;
}
@@ -606,6 +144,7 @@
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Failed to launch voice dialer: " + e);
}
}
return true;
@@ -614,7 +153,7 @@
long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
// Long press detected, call voice mail
- callVoicemail();
+ mFragment.callVoicemail();
}
return true;
}
@@ -626,631 +165,13 @@
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_CALL: {
- dialButtonPressed();
+ mFragment.dialButtonPressed();
return true;
}
}
return super.onKeyUp(keyCode, event);
}
- private void keyPressed(int keyCode) {
- mHaptic.vibrate();
- KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
- mDigits.onKeyDown(keyCode, event);
- }
-
- public boolean onKey(View view, int keyCode, KeyEvent event) {
- switch (view.getId()) {
- case R.id.digits:
- if (keyCode == KeyEvent.KEYCODE_ENTER) {
- dialButtonPressed();
- return true;
- }
- break;
- }
- return false;
- }
-
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.one: {
- playTone(ToneGenerator.TONE_DTMF_1);
- keyPressed(KeyEvent.KEYCODE_1);
- return;
- }
- case R.id.two: {
- playTone(ToneGenerator.TONE_DTMF_2);
- keyPressed(KeyEvent.KEYCODE_2);
- return;
- }
- case R.id.three: {
- playTone(ToneGenerator.TONE_DTMF_3);
- keyPressed(KeyEvent.KEYCODE_3);
- return;
- }
- case R.id.four: {
- playTone(ToneGenerator.TONE_DTMF_4);
- keyPressed(KeyEvent.KEYCODE_4);
- return;
- }
- case R.id.five: {
- playTone(ToneGenerator.TONE_DTMF_5);
- keyPressed(KeyEvent.KEYCODE_5);
- return;
- }
- case R.id.six: {
- playTone(ToneGenerator.TONE_DTMF_6);
- keyPressed(KeyEvent.KEYCODE_6);
- return;
- }
- case R.id.seven: {
- playTone(ToneGenerator.TONE_DTMF_7);
- keyPressed(KeyEvent.KEYCODE_7);
- return;
- }
- case R.id.eight: {
- playTone(ToneGenerator.TONE_DTMF_8);
- keyPressed(KeyEvent.KEYCODE_8);
- return;
- }
- case R.id.nine: {
- playTone(ToneGenerator.TONE_DTMF_9);
- keyPressed(KeyEvent.KEYCODE_9);
- return;
- }
- case R.id.zero: {
- playTone(ToneGenerator.TONE_DTMF_0);
- keyPressed(KeyEvent.KEYCODE_0);
- return;
- }
- case R.id.pound: {
- playTone(ToneGenerator.TONE_DTMF_P);
- keyPressed(KeyEvent.KEYCODE_POUND);
- return;
- }
- case R.id.star: {
- playTone(ToneGenerator.TONE_DTMF_S);
- keyPressed(KeyEvent.KEYCODE_STAR);
- return;
- }
- case R.id.deleteButton: {
- keyPressed(KeyEvent.KEYCODE_DEL);
- return;
- }
- case R.id.dialButton: {
- mHaptic.vibrate(); // Vibrate here too, just like we do for the regular keys
- dialButtonPressed();
- return;
- }
- case R.id.voicemailButton: {
- callVoicemail();
- mHaptic.vibrate();
- return;
- }
- case R.id.digits: {
- if (!isDigitsEmpty()) {
- mDigits.setCursorVisible(true);
- }
- return;
- }
- }
- }
-
- public boolean onLongClick(View view) {
- final Editable digits = mDigits.getText();
- int id = view.getId();
- switch (id) {
- case R.id.deleteButton: {
- digits.clear();
- // TODO: The framework forgets to clear the pressed
- // status of disabled button. Until this is fixed,
- // clear manually the pressed status. b/2133127
- mDelete.setPressed(false);
- return true;
- }
- case R.id.one: {
- if (isDigitsEmpty()) {
- callVoicemail();
- return true;
- }
- return false;
- }
- case R.id.zero: {
- keyPressed(KeyEvent.KEYCODE_PLUS);
- return true;
- }
- }
- return false;
- }
-
- void callVoicemail() {
- startActivity(newVoicemailIntent());
- mDigits.getText().clear(); // TODO: Fix bug 1745781
- finish();
- }
-
- /**
- * In most cases, when the dial button is pressed, there is a
- * number in digits area. Pack it in the intent, start the
- * outgoing call broadcast as a separate task and finish this
- * activity.
- *
- * When there is no digit and the phone is CDMA and off hook,
- * we're sending a blank flash for CDMA. CDMA networks use Flash
- * messages when special processing needs to be done, mainly for
- * 3-way or call waiting scenarios. Presumably, here we're in a
- * special 3-way scenario where the network needs a blank flash
- * before being able to add the new participant. (This is not the
- * case with all 3-way calls, just certain CDMA infrastructures.)
- *
- * Otherwise, there is no digit, display the last dialed
- * number. Don't finish since the user may want to edit it. The
- * user needs to press the dial button again, to dial it (general
- * case described above).
- */
- void dialButtonPressed() {
- if (isDigitsEmpty()) { // No number entered.
- if (phoneIsCdma() && phoneIsOffhook()) {
- // This is really CDMA specific. On GSM is it possible
- // to be off hook and wanted to add a 3rd party using
- // the redial feature.
- startActivity(newFlashIntent());
- } else {
- if (!TextUtils.isEmpty(mLastNumberDialed)) {
- mDigits.setText(mLastNumberDialed);
- } else {
- // There's no "last number dialed" or the
- // background query is still running. There's
- // nothing useful for the Dial button to do in
- // this case. Note: with a soft dial button, this
- // can never happens since the dial button is
- // disabled under these conditons.
- playTone(ToneGenerator.TONE_PROP_NACK);
- }
- }
- } else {
- final String number = mDigits.getText().toString();
-
- startActivity(newDialNumberIntent(number));
- mDigits.getText().clear(); // TODO: Fix bug 1745781
- finish();
- }
- }
-
-
- /**
- * Plays the specified tone for TONE_LENGTH_MS milliseconds.
- *
- * The tone is played locally, using the audio stream for phone calls.
- * Tones are played only if the "Audible touch tones" user preference
- * is checked, and are NOT played if the device is in silent mode.
- *
- * @param tone a tone code from {@link ToneGenerator}
- */
- void playTone(int tone) {
- // if local tone playback is disabled, just return.
- if (!mDTMFToneEnabled) {
- return;
- }
-
- // Also do nothing if the phone is in silent mode.
- // We need to re-check the ringer mode for *every* playTone()
- // call, rather than keeping a local flag that's updated in
- // onResume(), since it's possible to toggle silent mode without
- // leaving the current activity (via the ENDCALL-longpress menu.)
- AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- int ringerMode = audioManager.getRingerMode();
- if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
- || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
- return;
- }
-
- synchronized(mToneGeneratorLock) {
- if (mToneGenerator == null) {
- Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
- return;
- }
-
- // Start the new tone (will stop any playing tone)
- mToneGenerator.startTone(tone, TONE_LENGTH_MS);
- }
- }
-
- /**
- * Brings up the "dialpad chooser" UI in place of the usual Dialer
- * elements (the textfield/button and the dialpad underneath).
- *
- * We show this UI if the user brings up the Dialer while a call is
- * already in progress, since there's a good chance we got here
- * accidentally (and the user really wanted the in-call dialpad instead).
- * So in this situation we display an intermediate UI that lets the user
- * explicitly choose between the in-call dialpad ("Use touch tone
- * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
- * to call in progress" just goes back to the in-call UI with no dialpad
- * at all.)
- *
- * @param enabled If true, show the "dialpad chooser" instead
- * of the regular Dialer UI
- */
- private void showDialpadChooser(boolean enabled) {
- if (enabled) {
- // Log.i(TAG, "Showing dialpad chooser!");
- mDigits.setVisibility(View.GONE);
- if (mDialpad != null) mDialpad.setVisibility(View.GONE);
- mVoicemailDialAndDeleteRow.setVisibility(View.GONE);
- mDialpadChooser.setVisibility(View.VISIBLE);
-
- // Instantiate the DialpadChooserAdapter and hook it up to the
- // ListView. We do this only once.
- if (mDialpadChooserAdapter == null) {
- mDialpadChooserAdapter = new DialpadChooserAdapter(this);
- mDialpadChooser.setAdapter(mDialpadChooserAdapter);
- }
- } else {
- // Log.i(TAG, "Displaying normal Dialer UI.");
- mDigits.setVisibility(View.VISIBLE);
- if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
- mVoicemailDialAndDeleteRow.setVisibility(View.VISIBLE);
- mDialpadChooser.setVisibility(View.GONE);
- }
- }
-
- /**
- * @return true if we're currently showing the "dialpad chooser" UI.
- */
- private boolean dialpadChooserVisible() {
- return mDialpadChooser.getVisibility() == View.VISIBLE;
- }
-
- /**
- * Simple list adapter, binding to an icon + text label
- * for each item in the "dialpad chooser" list.
- */
- private static class DialpadChooserAdapter extends BaseAdapter {
- private LayoutInflater mInflater;
-
- // Simple struct for a single "choice" item.
- static class ChoiceItem {
- String text;
- Bitmap icon;
- int id;
-
- public ChoiceItem(String s, Bitmap b, int i) {
- text = s;
- icon = b;
- id = i;
- }
- }
-
- // IDs for the possible "choices":
- static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
- static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
- static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
-
- private static final int NUM_ITEMS = 3;
- private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
-
- public DialpadChooserAdapter(Context context) {
- // Cache the LayoutInflate to avoid asking for a new one each time.
- mInflater = LayoutInflater.from(context);
-
- // Initialize the possible choices.
- // TODO: could this be specified entirely in XML?
-
- // - "Use touch tone keypad"
- mChoiceItems[0] = new ChoiceItem(
- context.getString(R.string.dialer_useDtmfDialpad),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_tt_keypad),
- DIALPAD_CHOICE_USE_DTMF_DIALPAD);
-
- // - "Return to call in progress"
- mChoiceItems[1] = new ChoiceItem(
- context.getString(R.string.dialer_returnToInCallScreen),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_current_call),
- DIALPAD_CHOICE_RETURN_TO_CALL);
-
- // - "Add call"
- mChoiceItems[2] = new ChoiceItem(
- context.getString(R.string.dialer_addAnotherCall),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_add_call),
- DIALPAD_CHOICE_ADD_NEW_CALL);
- }
-
- public int getCount() {
- return NUM_ITEMS;
- }
-
- /**
- * Return the ChoiceItem for a given position.
- */
- public Object getItem(int position) {
- return mChoiceItems[position];
- }
-
- /**
- * Return a unique ID for each possible choice.
- */
- public long getItemId(int position) {
- return position;
- }
-
- /**
- * Make a view for each row.
- */
- public View getView(int position, View convertView, ViewGroup parent) {
- // When convertView is non-null, we can reuse it (there's no need
- // to reinflate it.)
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
- }
-
- TextView text = (TextView) convertView.findViewById(R.id.text);
- text.setText(mChoiceItems[position].text);
-
- ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
- icon.setImageBitmap(mChoiceItems[position].icon);
-
- return convertView;
- }
- }
-
- /**
- * Handle clicks from the dialpad chooser.
- */
- public void onItemClick(AdapterView parent, View v, int position, long id) {
- DialpadChooserAdapter.ChoiceItem item =
- (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
- int itemId = item.id;
- switch (itemId) {
- case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
- // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
- // Fire off an intent to go back to the in-call UI
- // with the dialpad visible.
- returnToInCallScreen(true);
- break;
-
- case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
- // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
- // Fire off an intent to go back to the in-call UI
- // (with the dialpad hidden).
- returnToInCallScreen(false);
- break;
-
- case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
- // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
- // Ok, guess the user really did want to be here (in the
- // regular Dialer) after all. Bring back the normal Dialer UI.
- showDialpadChooser(false);
- break;
-
- default:
- Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
- break;
- }
- }
-
- /**
- * Returns to the in-call UI (where there's presumably a call in
- * progress) in response to the user selecting "use touch tone keypad"
- * or "return to call" from the dialpad chooser.
- */
- private void returnToInCallScreen(boolean showDialpad) {
- try {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
- } catch (RemoteException e) {
- Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
- }
-
- // Finally, finish() ourselves so that we don't stay on the
- // activity stack.
- // Note that we do this whether or not the showCallScreenWithDialpad()
- // call above had any effect or not! (That call is a no-op if the
- // phone is idle, which can happen if the current call ends while
- // the dialpad chooser is up. In this case we can't show the
- // InCallScreen, and there's no point staying here in the Dialer,
- // so we just take the user back where he came from...)
- finish();
- }
-
- /**
- * @return true if the phone is "in use", meaning that at least one line
- * is active (ie. off hook or ringing or dialing).
- */
- private boolean phoneIsInUse() {
- boolean phoneInUse = false;
- try {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- if (phone != null) phoneInUse = !phone.isIdle();
- } catch (RemoteException e) {
- Log.w(TAG, "phone.isIdle() failed", e);
- }
- return phoneInUse;
- }
-
- /**
- * @return true if the phone is a CDMA phone type
- */
- private boolean phoneIsCdma() {
- boolean isCdma = false;
- try {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- if (phone != null) {
- isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "phone.getActivePhoneType() failed", e);
- }
- return isCdma;
- }
-
- /**
- * @return true if the phone state is OFFHOOK
- */
- private boolean phoneIsOffhook() {
- boolean phoneOffhook = false;
- try {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- if (phone != null) phoneOffhook = phone.isOffhook();
- } catch (RemoteException e) {
- Log.w(TAG, "phone.isOffhook() failed", e);
- }
- return phoneOffhook;
- }
-
-
- /**
- * Returns true whenever any one of the options from the menu is selected.
- * Code changes to support dialpad options
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case MENU_2S_PAUSE:
- updateDialString(",");
- return true;
- case MENU_WAIT:
- updateDialString(";");
- return true;
- }
- return false;
- }
-
- /**
- * Updates the dial string (mDigits) after inserting a Pause character (,)
- * or Wait character (;).
- */
- private void updateDialString(String newDigits) {
- int selectionStart;
- int selectionEnd;
-
- // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
- int anchor = mDigits.getSelectionStart();
- int point = mDigits.getSelectionEnd();
-
- selectionStart = Math.min(anchor, point);
- selectionEnd = Math.max(anchor, point);
-
- Editable digits = mDigits.getText();
- if (selectionStart != -1 ) {
- if (selectionStart == selectionEnd) {
- // then there is no selection. So insert the pause at this
- // position and update the mDigits.
- digits.replace(selectionStart, selectionStart, newDigits);
- } else {
- digits.replace(selectionStart, selectionEnd, newDigits);
- // Unselect: back to a regular cursor, just pass the character inserted.
- mDigits.setSelection(selectionStart + 1);
- }
- } else {
- int len = mDigits.length();
- digits.replace(len, len, newDigits);
- }
- }
-
- /**
- * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
- */
- private void updateDialAndDeleteButtonEnabledState() {
- final boolean digitsNotEmpty = !isDigitsEmpty();
-
- if (mDialButton != null) {
- // On CDMA phones, if we're already on a call, we *always*
- // enable the Dial button (since you can press it without
- // entering any digits to send an empty flash.)
- if (phoneIsCdma() && phoneIsOffhook()) {
- mDialButton.setEnabled(true);
- } else {
- // Common case: GSM, or CDMA but not on a call.
- // Enable the Dial button if some digits have
- // been entered, or if there is a last dialed number
- // that could be redialed.
- mDialButton.setEnabled(digitsNotEmpty ||
- !TextUtils.isEmpty(mLastNumberDialed));
- }
- }
- mDelete.setEnabled(digitsNotEmpty);
- }
-
-
- /**
- * Check if voicemail is enabled/accessible.
- */
- private void initVoicemailButton() {
- boolean hasVoicemail = false;
- try {
- hasVoicemail = TelephonyManager.getDefault().getVoiceMailNumber() != null;
- } catch (SecurityException se) {
- // Possibly no READ_PHONE_STATE privilege.
- }
-
- mVoicemailButton = mVoicemailDialAndDeleteRow.findViewById(R.id.voicemailButton);
- if (hasVoicemail) {
- mVoicemailButton.setOnClickListener(this);
- } else {
- mVoicemailButton.setEnabled(false);
- }
- }
-
- /**
- * This function return true if Wait menu item can be shown
- * otherwise returns false. Assumes the passed string is non-empty
- * and the 0th index check is not required.
- */
- private boolean showWait(int start, int end, String digits) {
- if (start == end) {
- // visible false in this case
- if (start > digits.length()) return false;
-
- // preceding char is ';', so visible should be false
- if (digits.charAt(start-1) == ';') return false;
-
- // next char is ';', so visible should be false
- if ((digits.length() > start) && (digits.charAt(start) == ';')) return false;
- } else {
- // visible false in this case
- if (start > digits.length() || end > digits.length()) return false;
-
- // In this case we need to just check for ';' preceding to start
- // or next to end
- if (digits.charAt(start-1) == ';') return false;
- }
- return true;
- }
-
- /**
- * @return true if the widget with the phone number digits is empty.
- */
- private boolean isDigitsEmpty() {
- return mDigits.length() == 0;
- }
-
- /**
- * Starts the asyn query to get the last dialed/outgoing
- * number. When the background query finishes, mLastNumberDialed
- * is set to the last dialed number or an empty string if none
- * exists yet.
- */
- private void queryLastOutgoingCall() {
- mLastNumberDialed = EMPTY_NUMBER;
- CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
- new CallLogAsync.GetLastOutgoingCallArgs(
- this,
- new CallLogAsync.OnLastOutgoingCallComplete() {
- public void lastOutgoingCall(String number) {
- // TODO: Filter out emergency numbers if
- // the carrier does not want redial for
- // these.
- mLastNumberDialed = number;
- updateDialAndDeleteButtonEnabledState();
- }
- });
- mCallLog.getLastOutgoingCall(lastCallArgs);
- }
-
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
boolean globalSearch) {
@@ -1260,25 +181,4 @@
ContactsSearchManager.startSearch(this, initialQuery);
}
}
-
- // Helpers for the call intents.
- private Intent newVoicemailIntent() {
- final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- Uri.fromParts("voicemail", EMPTY_NUMBER, null));
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
- }
-
- private Intent newFlashIntent() {
- final Intent intent = newDialNumberIntent(EMPTY_NUMBER);
- intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
- return intent;
- }
-
- private Intent newDialNumberIntent(String number) {
- final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- Uri.fromParts("tel", number, null));
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
- }
}
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 5054309..869ae2a 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -51,7 +51,7 @@
private static final int TAB_INDEX_CONTACTS = 2;
private static final int TAB_INDEX_FAVORITES = 3;
- static final String EXTRA_IGNORE_STATE = "ignore-state";
+ public static final String EXTRA_IGNORE_STATE = "ignore-state";
/** Name of the dialtacts shared preferences */
static final String PREFS_DIALTACTS = "dialtacts";
@@ -91,6 +91,7 @@
setupCallLogTab();
setupContactsTab();
setupFavoritesTab();
+ setupGroupsTab();
// Load the last manually loaded tab
final SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
@@ -172,6 +173,18 @@
.setContent(intent));
}
+ private void setupGroupsTab() {
+ // This is a temporary intent action until the refactoring for the phone/contacts
+ // split is complete.
+ Intent intent = new Intent("com.android.phone.action.GROUPS_LIST");
+ intent.setClass(this, GroupBrowserActivity.class);
+
+ mTabHost.addTab(mTabHost.newTabSpec("groups")
+ .setIndicator(getString(R.string.contactsGroupsLabel),
+ getResources().getDrawable(R.drawable.ic_menu_display_all_holo_light))
+ .setContent(intent));
+ }
+
/**
* Returns true if the intent is due to hitting the green send key while in a call.
*
diff --git a/src/com/android/contacts/activities/GroupBrowserActivity.java b/src/com/android/contacts/activities/GroupBrowserActivity.java
new file mode 100644
index 0000000..e8b3ad8
--- /dev/null
+++ b/src/com/android/contacts/activities/GroupBrowserActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.activities;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Displays a list to browse groups.
+ */
+public class GroupBrowserActivity extends ContactsActivity {
+
+ private static final String TAG = "GroupBrowserActivity";
+
+ public GroupBrowserActivity() {
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ configureContentView(true, savedState);
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ setIntent(intent);
+ configureContentView(false, null);
+ }
+
+ private void configureContentView(boolean createContentView, Bundle savedState) {
+ // TODO: Create Intent Resolver to handle the different ways users can get to this list.
+ // TODO: Use savedState if necessary
+ // TODO: Setup action bar
+ if (createContentView) {
+ setContentView(R.layout.group_browser_activity);
+ }
+ }
+}
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
new file mode 100644
index 0000000..fc5fcd9
--- /dev/null
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -0,0 +1,1219 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.dialpad;
+
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
+import com.android.contacts.SpecialCharSequenceMgr;
+import com.android.contacts.activities.DialtactsActivity;
+import com.android.internal.telephony.ITelephony;
+import com.android.phone.CallLogAsync;
+import com.android.phone.HapticFeedback;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Contacts.Intents.Insert;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.PhonesColumns;
+import android.provider.Settings;
+import android.telephony.PhoneNumberFormattingTextWatcher;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.method.DialerKeyListener;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Fragment that displays a twelve-key phone dialpad.
+ */
+public class DialpadFragment extends Fragment
+ implements View.OnClickListener,
+ View.OnLongClickListener, View.OnKeyListener,
+ AdapterView.OnItemClickListener, TextWatcher {
+ private static final String TAG = "DialpadFragment";
+
+ private static final String EMPTY_NUMBER = "";
+
+ /** The length of DTMF tones in milliseconds */
+ private static final int TONE_LENGTH_MS = 150;
+
+ /** The DTMF tone volume relative to other sounds in the stream */
+ private static final int TONE_RELATIVE_VOLUME = 80;
+
+ /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
+ private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+
+ private EditText mDigits;
+ private View mDelete;
+ private MenuItem mAddToContactMenuItem;
+ private ToneGenerator mToneGenerator;
+ private Object mToneGeneratorLock = new Object();
+ private Drawable mDigitsBackground;
+ private Drawable mDigitsEmptyBackground;
+ private View mDialpad;
+ private View mVoicemailDialAndDeleteRow;
+ private View mVoicemailButton;
+ private View mDialButton;
+ private ListView mDialpadChooser;
+ private DialpadChooserAdapter mDialpadChooserAdapter;
+ //Member variables for dialpad options
+ private MenuItem m2SecPauseMenuItem;
+ private MenuItem mWaitMenuItem;
+ private static final int MENU_ADD_CONTACTS = 1;
+ private static final int MENU_2S_PAUSE = 2;
+ private static final int MENU_WAIT = 3;
+
+ // Last number dialed, retrieved asynchronously from the call DB
+ // in onCreate. This number is displayed when the user hits the
+ // send key and cleared in onPause.
+ CallLogAsync mCallLog = new CallLogAsync();
+ private String mLastNumberDialed = EMPTY_NUMBER;
+
+ // determines if we want to playback local DTMF tones.
+ private boolean mDTMFToneEnabled;
+
+ // Vibration (haptic feedback) for dialer key presses.
+ private HapticFeedback mHaptic = new HapticFeedback();
+
+ /** Identifier for the "Add Call" intent extra. */
+ static final String ADD_CALL_MODE_KEY = "add_call_mode";
+
+ /**
+ * Identifier for intent extra for sending an empty Flash message for
+ * CDMA networks. This message is used by the network to simulate a
+ * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
+ *
+ * TODO: Using an intent extra to tell the phone to send this flash is a
+ * temporary measure. To be replaced with an ITelephony call in the future.
+ * TODO: Keep in sync with the string defined in OutgoingCallBroadcaster.java
+ * in Phone app until this is replaced with the ITelephony API.
+ */
+ static final String EXTRA_SEND_EMPTY_FLASH
+ = "com.android.phone.extra.SEND_EMPTY_FLASH";
+
+ /** Indicates if we are opening this dialer to add a call from the InCallScreen. */
+ private boolean mIsAddCallMode;
+
+ private String mCurrentCountryIso;
+
+ PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ /**
+ * Listen for phone state changes so that we can take down the
+ * "dialpad chooser" if the phone becomes idle while the
+ * chooser UI is visible.
+ */
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
+ // + state + ", '" + incomingNumber + "'");
+ if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
+ // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
+ // Note there's a race condition in the UI here: the
+ // dialpad chooser could conceivably disappear (on its
+ // own) at the exact moment the user was trying to select
+ // one of the choices, which would be confusing. (But at
+ // least that's better than leaving the dialpad chooser
+ // onscreen, but useless...)
+ showDialpadChooser(false);
+ }
+ }
+ };
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Do nothing
+ }
+
+ public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
+ // Do nothing
+ // DTMF Tones do not need to be played here any longer -
+ // the DTMF dialer handles that functionality now.
+ }
+
+ public void afterTextChanged(Editable input) {
+ if (SpecialCharSequenceMgr.handleChars(getActivity(), input.toString(), mDigits)) {
+ // A special sequence was entered, clear the digits
+ mDigits.getText().clear();
+ }
+
+ if (!isDigitsEmpty()) {
+ mDigits.setBackgroundDrawable(mDigitsBackground);
+ } else {
+ mDigits.setCursorVisible(false);
+ mDigits.setBackgroundDrawable(mDigitsEmptyBackground);
+ }
+
+ updateDialAndDeleteButtonEnabledState();
+ }
+
+ @Override
+ public void onCreate(Bundle state) {
+ super.onCreate(state);
+
+ mCurrentCountryIso = ContactsUtils.getCurrentCountryIso(getActivity());
+
+ try {
+ mHaptic.init(getActivity(),
+ getResources().getBoolean(R.bool.config_enable_dialer_key_vibration));
+ } catch (Resources.NotFoundException nfe) {
+ Log.e(TAG, "Vibrate control bool missing.", nfe);
+ }
+
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ View fragmentView = inflater.inflate(R.layout.dialpad_fragment, container, false);
+
+ // Load up the resources for the text field.
+ Resources r = getResources();
+ mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
+ mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
+
+ mDigits = (EditText) fragmentView.findViewById(R.id.digits);
+ mDigits.setKeyListener(DialerKeyListener.getInstance());
+ mDigits.setOnClickListener(this);
+ mDigits.setOnKeyListener(this);
+
+ maybeAddNumberFormatting();
+
+ // Check for the presence of the keypad
+ View oneButton = fragmentView.findViewById(R.id.one);
+ if (oneButton != null) {
+ setupKeypad(fragmentView);
+ }
+
+ mVoicemailDialAndDeleteRow = fragmentView.findViewById(R.id.voicemailAndDialAndDelete);
+
+ initVoicemailButton();
+
+ // Check whether we should show the onscreen "Dial" button.
+ mDialButton = mVoicemailDialAndDeleteRow.findViewById(R.id.dialButton);
+
+ if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
+ mDialButton.setOnClickListener(this);
+ } else {
+ mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
+ mDialButton = null;
+ }
+
+ mDelete = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
+ mDelete.setOnClickListener(this);
+ mDelete.setOnLongClickListener(this);
+
+ mDialpad = fragmentView.findViewById(R.id.dialpad); // This is null in landscape mode.
+
+ // In landscape we put the keyboard in phone mode.
+ // In portrait we prevent the soft keyboard to show since the
+ // dialpad acts as one already.
+ if (null == mDialpad) {
+ mDigits.setInputType(android.text.InputType.TYPE_CLASS_PHONE);
+ } else {
+ mDigits.setInputType(android.text.InputType.TYPE_NULL);
+ }
+
+ // Set up the "dialpad chooser" UI; see showDialpadChooser().
+ mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser);
+ mDialpadChooser.setOnItemClickListener(this);
+
+ return fragmentView;
+ }
+
+ public EditText getDigitsWidget() {
+ return mDigits;
+ }
+
+ private void maybeAddNumberFormatting() {
+ mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher(mCurrentCountryIso));
+ }
+
+ /**
+ * Handles the intent that launched us.
+ *
+ * We can be launched either with ACTION_DIAL or ACTION_VIEW (which
+ * may include a phone number to pre-load), or ACTION_MAIN (which just
+ * brings up a blank dialpad).
+ *
+ * @return true IFF the current intent has the DialtactsActivity.EXTRA_IGNORE_STATE
+ * extra set to true, which indicates (to our container) that we should ignore
+ * any possible saved state, and instead reset our state based on the parent's
+ * intent.
+ */
+ public boolean resolveIntent() {
+ boolean ignoreState = false;
+
+ // Find the proper intent
+ final Intent intent;
+ if (getActivity().isChild()) {
+ intent = getActivity().getParent().getIntent();
+ ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
+ } else {
+ intent = getActivity().getIntent();
+ }
+ // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
+
+ // by default we are not adding a call.
+ mIsAddCallMode = false;
+
+ // By default we don't show the "dialpad chooser" UI.
+ boolean needToShowDialpadChooser = false;
+
+ // Resolve the intent
+ final String action = intent.getAction();
+ if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
+ // see if we are "adding a call" from the InCallScreen; false by default.
+ mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
+
+ Uri uri = intent.getData();
+ if (uri != null) {
+ if ("tel".equals(uri.getScheme())) {
+ // Put the requested number into the input area
+ String data = uri.getSchemeSpecificPart();
+ setFormattedDigits(data, null);
+ } else {
+ String type = intent.getType();
+ if (People.CONTENT_ITEM_TYPE.equals(type)
+ || Phones.CONTENT_ITEM_TYPE.equals(type)) {
+ // Query the phone number
+ Cursor c = getActivity().getContentResolver().query(intent.getData(),
+ new String[] {PhonesColumns.NUMBER, PhonesColumns.NUMBER_KEY},
+ null, null, null);
+ if (c != null) {
+ if (c.moveToFirst()) {
+ // Put the number into the input area
+ setFormattedDigits(c.getString(0), c.getString(1));
+ }
+ c.close();
+ }
+ }
+ }
+ } else {
+ // ACTION_DIAL or ACTION_VIEW with no data.
+ // This behaves basically like ACTION_MAIN: If there's
+ // already an active call, bring up an intermediate UI to
+ // make the user confirm what they really want to do.
+ // Be sure *not* to show the dialpad chooser if this is an
+ // explicit "Add call" action, though.
+ if (!mIsAddCallMode && phoneIsInUse()) {
+ needToShowDialpadChooser = true;
+ }
+ }
+ } else if (Intent.ACTION_MAIN.equals(action)) {
+ // The MAIN action means we're bringing up a blank dialer
+ // (e.g. by selecting the Home shortcut, or tabbing over from
+ // Contacts or Call log.)
+ //
+ // At this point, IF there's already an active call, there's a
+ // good chance that the user got here accidentally (but really
+ // wanted the in-call dialpad instead). So we bring up an
+ // intermediate UI to make the user confirm what they really
+ // want to do.
+ if (phoneIsInUse()) {
+ // Log.i(TAG, "resolveIntent(): phone is in use; showing dialpad chooser!");
+ needToShowDialpadChooser = true;
+ }
+ }
+
+ // Bring up the "dialpad chooser" IFF we need to make the user
+ // confirm which dialpad they really want.
+ showDialpadChooser(needToShowDialpadChooser);
+
+ return ignoreState;
+ }
+
+ private void setFormattedDigits(String data, String normalizedNumber) {
+ // strip the non-dialable numbers out of the data string.
+ String dialString = PhoneNumberUtils.extractNetworkPortion(data);
+ dialString =
+ PhoneNumberUtils.formatNumber(dialString, normalizedNumber, mCurrentCountryIso);
+ if (!TextUtils.isEmpty(dialString)) {
+ Editable digits = mDigits.getText();
+ digits.replace(0, digits.length(), dialString);
+ // for some reason this isn't getting called in the digits.replace call above..
+ // but in any case, this will make sure the background drawable looks right
+ afterTextChanged(digits);
+ }
+ }
+
+ private void setupKeypad(View fragmentView) {
+ // Setup the listeners for the buttons
+ View view = fragmentView.findViewById(R.id.one);
+ view.setOnClickListener(this);
+ view.setOnLongClickListener(this);
+
+ fragmentView.findViewById(R.id.two).setOnClickListener(this);
+ fragmentView.findViewById(R.id.three).setOnClickListener(this);
+ fragmentView.findViewById(R.id.four).setOnClickListener(this);
+ fragmentView.findViewById(R.id.five).setOnClickListener(this);
+ fragmentView.findViewById(R.id.six).setOnClickListener(this);
+ fragmentView.findViewById(R.id.seven).setOnClickListener(this);
+ fragmentView.findViewById(R.id.eight).setOnClickListener(this);
+ fragmentView.findViewById(R.id.nine).setOnClickListener(this);
+ fragmentView.findViewById(R.id.star).setOnClickListener(this);
+
+ view = fragmentView.findViewById(R.id.zero);
+ view.setOnClickListener(this);
+ view.setOnLongClickListener(this);
+
+ fragmentView.findViewById(R.id.pound).setOnClickListener(this);
+ }
+
+ // Do some stuff that needs to happen only once, but which we
+ // can't do directly from onCreate().
+ public void onPostCreate() {
+ // This can't be done in onCreate(), since the auto-restoring of the digits
+ // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
+ // is called. This method will be called every time the activity is created, and
+ // will always happen after onRestoreSavedInstanceState().
+ mDigits.addTextChangedListener(this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // Query the last dialed number. Do it first because hitting
+ // the DB is 'slow'. This call is asynchronous.
+ queryLastOutgoingCall();
+
+ // retrieve the DTMF tone play back setting.
+ mDTMFToneEnabled = Settings.System.getInt(getActivity().getContentResolver(),
+ Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
+
+ // Retrieve the haptic feedback setting.
+ mHaptic.checkSystemSetting();
+
+ // if the mToneGenerator creation fails, just continue without it. It is
+ // a local audio signal, and is not as important as the dtmf tone itself.
+ synchronized (mToneGeneratorLock) {
+ if (mToneGenerator == null) {
+ try {
+ // we want the user to be able to control the volume of the dial tones
+ // outside of a call, so we use the stream type that is also mapped to the
+ // volume control keys for this activity
+ mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
+ getActivity().setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Exception caught while creating local tone generator: " + e);
+ mToneGenerator = null;
+ }
+ }
+ }
+
+ Activity parent = getActivity().getParent();
+ // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
+ // digits in the dialer field.
+ if (parent != null && parent instanceof DialtactsActivity) {
+ Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
+ if (dialUri != null) {
+ resolveIntent();
+ }
+ }
+
+ // While we're in the foreground, listen for phone state changes,
+ // purely so that we can take down the "dialpad chooser" if the
+ // phone becomes idle while the chooser UI is visible.
+ TelephonyManager telephonyManager =
+ (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
+ telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+
+ // Potentially show hint text in the mDigits field when the user
+ // hasn't typed any digits yet. (If there's already an active call,
+ // this hint text will remind the user that he's about to add a new
+ // call.)
+ //
+ // TODO: consider adding better UI for the case where *both* lines
+ // are currently in use. (Right now we let the user try to add
+ // another call, but that call is guaranteed to fail. Perhaps the
+ // entire dialer UI should be disabled instead.)
+ if (phoneIsInUse()) {
+ mDigits.setHint(R.string.dialerDialpadHintText);
+ } else {
+ // Common case; no hint necessary.
+ mDigits.setHint(null);
+
+ // Also, a sanity-check: the "dialpad chooser" UI should NEVER
+ // be visible if the phone is idle!
+ showDialpadChooser(false);
+ }
+
+ updateDialAndDeleteButtonEnabledState();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ // Stop listening for phone state changes.
+ TelephonyManager telephonyManager =
+ (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
+ telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+
+ synchronized (mToneGeneratorLock) {
+ if (mToneGenerator != null) {
+ mToneGenerator.release();
+ mToneGenerator = null;
+ }
+ }
+ // TODO: I wonder if we should not check if the AsyncTask that
+ // lookup the last dialed number has completed.
+ mLastNumberDialed = EMPTY_NUMBER; // Since we are going to query again, free stale number.
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+
+ mAddToContactMenuItem = menu.add(0, MENU_ADD_CONTACTS, 0, R.string.recentCalls_addToContact)
+ .setIcon(android.R.drawable.ic_menu_add);
+ m2SecPauseMenuItem = menu.add(0, MENU_2S_PAUSE, 0, R.string.add_2sec_pause)
+ .setIcon(R.drawable.ic_menu_2sec_pause);
+ mWaitMenuItem = menu.add(0, MENU_WAIT, 0, R.string.add_wait)
+ .setIcon(R.drawable.ic_menu_wait);
+ }
+
+ /** @return true if an options menu should be shown in our containing activity. */
+ public boolean allowOptionsMenu() {
+ // We never show a menu if the "choose dialpad" UI is up.
+ // Otherwise the menu is allowed (see onPrepareOptionsMenu() below.)
+ return (!dialpadChooserVisible());
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ // Note that we won't show a menu at all if the "choose dialpad" UI is up.
+ // (See allowOptionsMenu() above, along with DialogActivity.onPrepareOptionsMenu().)
+
+ if (isDigitsEmpty()) {
+ mAddToContactMenuItem.setVisible(false);
+ m2SecPauseMenuItem.setVisible(false);
+ mWaitMenuItem.setVisible(false);
+ } else {
+ CharSequence digits = mDigits.getText();
+
+ // Put the current digits string into an intent
+ Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ intent.putExtra(Insert.PHONE, digits);
+ intent.setType(People.CONTENT_ITEM_TYPE);
+ mAddToContactMenuItem.setIntent(intent);
+ mAddToContactMenuItem.setVisible(true);
+
+ // Check out whether to show Pause & Wait option menu items
+ int selectionStart;
+ int selectionEnd;
+ String strDigits = digits.toString();
+
+ selectionStart = mDigits.getSelectionStart();
+ selectionEnd = mDigits.getSelectionEnd();
+
+ if (selectionStart != -1) {
+ if (selectionStart > selectionEnd) {
+ // swap it as we want start to be less then end
+ int tmp = selectionStart;
+ selectionStart = selectionEnd;
+ selectionEnd = tmp;
+ }
+
+ if (selectionStart != 0) {
+ // Pause can be visible if cursor is not in the begining
+ m2SecPauseMenuItem.setVisible(true);
+
+ // For Wait to be visible set of condition to meet
+ mWaitMenuItem.setVisible(showWait(selectionStart,
+ selectionEnd, strDigits));
+ } else {
+ // cursor in the beginning both pause and wait to be invisible
+ m2SecPauseMenuItem.setVisible(false);
+ mWaitMenuItem.setVisible(false);
+ }
+ } else {
+ // cursor is not selected so assume new digit is added to the end
+ int strLength = strDigits.length();
+ mWaitMenuItem.setVisible(showWait(strLength,
+ strLength, strDigits));
+ }
+ }
+ }
+
+ private void keyPressed(int keyCode) {
+ mHaptic.vibrate();
+ KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
+ mDigits.onKeyDown(keyCode, event);
+ }
+
+ public boolean onKey(View view, int keyCode, KeyEvent event) {
+ switch (view.getId()) {
+ case R.id.digits:
+ if (keyCode == KeyEvent.KEYCODE_ENTER) {
+ dialButtonPressed();
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.one: {
+ playTone(ToneGenerator.TONE_DTMF_1);
+ keyPressed(KeyEvent.KEYCODE_1);
+ return;
+ }
+ case R.id.two: {
+ playTone(ToneGenerator.TONE_DTMF_2);
+ keyPressed(KeyEvent.KEYCODE_2);
+ return;
+ }
+ case R.id.three: {
+ playTone(ToneGenerator.TONE_DTMF_3);
+ keyPressed(KeyEvent.KEYCODE_3);
+ return;
+ }
+ case R.id.four: {
+ playTone(ToneGenerator.TONE_DTMF_4);
+ keyPressed(KeyEvent.KEYCODE_4);
+ return;
+ }
+ case R.id.five: {
+ playTone(ToneGenerator.TONE_DTMF_5);
+ keyPressed(KeyEvent.KEYCODE_5);
+ return;
+ }
+ case R.id.six: {
+ playTone(ToneGenerator.TONE_DTMF_6);
+ keyPressed(KeyEvent.KEYCODE_6);
+ return;
+ }
+ case R.id.seven: {
+ playTone(ToneGenerator.TONE_DTMF_7);
+ keyPressed(KeyEvent.KEYCODE_7);
+ return;
+ }
+ case R.id.eight: {
+ playTone(ToneGenerator.TONE_DTMF_8);
+ keyPressed(KeyEvent.KEYCODE_8);
+ return;
+ }
+ case R.id.nine: {
+ playTone(ToneGenerator.TONE_DTMF_9);
+ keyPressed(KeyEvent.KEYCODE_9);
+ return;
+ }
+ case R.id.zero: {
+ playTone(ToneGenerator.TONE_DTMF_0);
+ keyPressed(KeyEvent.KEYCODE_0);
+ return;
+ }
+ case R.id.pound: {
+ playTone(ToneGenerator.TONE_DTMF_P);
+ keyPressed(KeyEvent.KEYCODE_POUND);
+ return;
+ }
+ case R.id.star: {
+ playTone(ToneGenerator.TONE_DTMF_S);
+ keyPressed(KeyEvent.KEYCODE_STAR);
+ return;
+ }
+ case R.id.deleteButton: {
+ keyPressed(KeyEvent.KEYCODE_DEL);
+ return;
+ }
+ case R.id.dialButton: {
+ mHaptic.vibrate(); // Vibrate here too, just like we do for the regular keys
+ dialButtonPressed();
+ return;
+ }
+ case R.id.voicemailButton: {
+ callVoicemail();
+ mHaptic.vibrate();
+ return;
+ }
+ case R.id.digits: {
+ if (!isDigitsEmpty()) {
+ mDigits.setCursorVisible(true);
+ }
+ return;
+ }
+ }
+ }
+
+ public boolean onLongClick(View view) {
+ final Editable digits = mDigits.getText();
+ int id = view.getId();
+ switch (id) {
+ case R.id.deleteButton: {
+ digits.clear();
+ // TODO: The framework forgets to clear the pressed
+ // status of disabled button. Until this is fixed,
+ // clear manually the pressed status. b/2133127
+ mDelete.setPressed(false);
+ return true;
+ }
+ case R.id.one: {
+ if (isDigitsEmpty()) {
+ callVoicemail();
+ return true;
+ }
+ return false;
+ }
+ case R.id.zero: {
+ keyPressed(KeyEvent.KEYCODE_PLUS);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void callVoicemail() {
+ startActivity(newVoicemailIntent());
+ mDigits.getText().clear(); // TODO: Fix bug 1745781
+ getActivity().finish();
+ }
+
+ /**
+ * In most cases, when the dial button is pressed, there is a
+ * number in digits area. Pack it in the intent, start the
+ * outgoing call broadcast as a separate task and finish this
+ * activity.
+ *
+ * When there is no digit and the phone is CDMA and off hook,
+ * we're sending a blank flash for CDMA. CDMA networks use Flash
+ * messages when special processing needs to be done, mainly for
+ * 3-way or call waiting scenarios. Presumably, here we're in a
+ * special 3-way scenario where the network needs a blank flash
+ * before being able to add the new participant. (This is not the
+ * case with all 3-way calls, just certain CDMA infrastructures.)
+ *
+ * Otherwise, there is no digit, display the last dialed
+ * number. Don't finish since the user may want to edit it. The
+ * user needs to press the dial button again, to dial it (general
+ * case described above).
+ */
+ public void dialButtonPressed() {
+ if (isDigitsEmpty()) { // No number entered.
+ if (phoneIsCdma() && phoneIsOffhook()) {
+ // This is really CDMA specific. On GSM is it possible
+ // to be off hook and wanted to add a 3rd party using
+ // the redial feature.
+ startActivity(newFlashIntent());
+ } else {
+ if (!TextUtils.isEmpty(mLastNumberDialed)) {
+ mDigits.setText(mLastNumberDialed);
+ } else {
+ // There's no "last number dialed" or the
+ // background query is still running. There's
+ // nothing useful for the Dial button to do in
+ // this case. Note: with a soft dial button, this
+ // can never happens since the dial button is
+ // disabled under these conditons.
+ playTone(ToneGenerator.TONE_PROP_NACK);
+ }
+ }
+ } else {
+ final String number = mDigits.getText().toString();
+
+ startActivity(newDialNumberIntent(number));
+ mDigits.getText().clear(); // TODO: Fix bug 1745781
+ getActivity().finish();
+ }
+ }
+
+ /**
+ * Plays the specified tone for TONE_LENGTH_MS milliseconds.
+ *
+ * The tone is played locally, using the audio stream for phone calls.
+ * Tones are played only if the "Audible touch tones" user preference
+ * is checked, and are NOT played if the device is in silent mode.
+ *
+ * @param tone a tone code from {@link ToneGenerator}
+ */
+ void playTone(int tone) {
+ // if local tone playback is disabled, just return.
+ if (!mDTMFToneEnabled) {
+ return;
+ }
+
+ // Also do nothing if the phone is in silent mode.
+ // We need to re-check the ringer mode for *every* playTone()
+ // call, rather than keeping a local flag that's updated in
+ // onResume(), since it's possible to toggle silent mode without
+ // leaving the current activity (via the ENDCALL-longpress menu.)
+ AudioManager audioManager =
+ (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int ringerMode = audioManager.getRingerMode();
+ if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
+ || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
+ return;
+ }
+
+ synchronized (mToneGeneratorLock) {
+ if (mToneGenerator == null) {
+ Log.w(TAG, "playTone: mToneGenerator == null, tone: " + tone);
+ return;
+ }
+
+ // Start the new tone (will stop any playing tone)
+ mToneGenerator.startTone(tone, TONE_LENGTH_MS);
+ }
+ }
+
+ /**
+ * Brings up the "dialpad chooser" UI in place of the usual Dialer
+ * elements (the textfield/button and the dialpad underneath).
+ *
+ * We show this UI if the user brings up the Dialer while a call is
+ * already in progress, since there's a good chance we got here
+ * accidentally (and the user really wanted the in-call dialpad instead).
+ * So in this situation we display an intermediate UI that lets the user
+ * explicitly choose between the in-call dialpad ("Use touch tone
+ * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
+ * to call in progress" just goes back to the in-call UI with no dialpad
+ * at all.)
+ *
+ * @param enabled If true, show the "dialpad chooser" instead
+ * of the regular Dialer UI
+ */
+ private void showDialpadChooser(boolean enabled) {
+ if (enabled) {
+ // Log.i(TAG, "Showing dialpad chooser!");
+ mDigits.setVisibility(View.GONE);
+ if (mDialpad != null) mDialpad.setVisibility(View.GONE);
+ mVoicemailDialAndDeleteRow.setVisibility(View.GONE);
+ mDialpadChooser.setVisibility(View.VISIBLE);
+
+ // Instantiate the DialpadChooserAdapter and hook it up to the
+ // ListView. We do this only once.
+ if (mDialpadChooserAdapter == null) {
+ mDialpadChooserAdapter = new DialpadChooserAdapter(getActivity());
+ mDialpadChooser.setAdapter(mDialpadChooserAdapter);
+ }
+ } else {
+ // Log.i(TAG, "Displaying normal Dialer UI.");
+ mDigits.setVisibility(View.VISIBLE);
+ if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
+ mVoicemailDialAndDeleteRow.setVisibility(View.VISIBLE);
+ mDialpadChooser.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * @return true if we're currently showing the "dialpad chooser" UI.
+ */
+ private boolean dialpadChooserVisible() {
+ return mDialpadChooser.getVisibility() == View.VISIBLE;
+ }
+
+ /**
+ * Simple list adapter, binding to an icon + text label
+ * for each item in the "dialpad chooser" list.
+ */
+ private static class DialpadChooserAdapter extends BaseAdapter {
+ private LayoutInflater mInflater;
+
+ // Simple struct for a single "choice" item.
+ static class ChoiceItem {
+ String text;
+ Bitmap icon;
+ int id;
+
+ public ChoiceItem(String s, Bitmap b, int i) {
+ text = s;
+ icon = b;
+ id = i;
+ }
+ }
+
+ // IDs for the possible "choices":
+ static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
+ static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
+ static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
+
+ private static final int NUM_ITEMS = 3;
+ private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
+
+ public DialpadChooserAdapter(Context context) {
+ // Cache the LayoutInflate to avoid asking for a new one each time.
+ mInflater = LayoutInflater.from(context);
+
+ // Initialize the possible choices.
+ // TODO: could this be specified entirely in XML?
+
+ // - "Use touch tone keypad"
+ mChoiceItems[0] = new ChoiceItem(
+ context.getString(R.string.dialer_useDtmfDialpad),
+ BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.ic_dialer_fork_tt_keypad),
+ DIALPAD_CHOICE_USE_DTMF_DIALPAD);
+
+ // - "Return to call in progress"
+ mChoiceItems[1] = new ChoiceItem(
+ context.getString(R.string.dialer_returnToInCallScreen),
+ BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.ic_dialer_fork_current_call),
+ DIALPAD_CHOICE_RETURN_TO_CALL);
+
+ // - "Add call"
+ mChoiceItems[2] = new ChoiceItem(
+ context.getString(R.string.dialer_addAnotherCall),
+ BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.ic_dialer_fork_add_call),
+ DIALPAD_CHOICE_ADD_NEW_CALL);
+ }
+
+ public int getCount() {
+ return NUM_ITEMS;
+ }
+
+ /**
+ * Return the ChoiceItem for a given position.
+ */
+ public Object getItem(int position) {
+ return mChoiceItems[position];
+ }
+
+ /**
+ * Return a unique ID for each possible choice.
+ */
+ public long getItemId(int position) {
+ return position;
+ }
+
+ /**
+ * Make a view for each row.
+ */
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // When convertView is non-null, we can reuse it (there's no need
+ // to reinflate it.)
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
+ }
+
+ TextView text = (TextView) convertView.findViewById(R.id.text);
+ text.setText(mChoiceItems[position].text);
+
+ ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+ icon.setImageBitmap(mChoiceItems[position].icon);
+
+ return convertView;
+ }
+ }
+
+ /**
+ * Handle clicks from the dialpad chooser.
+ */
+ public void onItemClick(AdapterView parent, View v, int position, long id) {
+ DialpadChooserAdapter.ChoiceItem item =
+ (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
+ int itemId = item.id;
+ switch (itemId) {
+ case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
+ // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
+ // Fire off an intent to go back to the in-call UI
+ // with the dialpad visible.
+ returnToInCallScreen(true);
+ break;
+
+ case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
+ // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
+ // Fire off an intent to go back to the in-call UI
+ // (with the dialpad hidden).
+ returnToInCallScreen(false);
+ break;
+
+ case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
+ // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
+ // Ok, guess the user really did want to be here (in the
+ // regular Dialer) after all. Bring back the normal Dialer UI.
+ showDialpadChooser(false);
+ break;
+
+ default:
+ Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
+ break;
+ }
+ }
+
+ /**
+ * Returns to the in-call UI (where there's presumably a call in
+ * progress) in response to the user selecting "use touch tone keypad"
+ * or "return to call" from the dialpad chooser.
+ */
+ private void returnToInCallScreen(boolean showDialpad) {
+ try {
+ ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+ if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
+ } catch (RemoteException e) {
+ Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
+ }
+
+ // Finally, finish() ourselves so that we don't stay on the
+ // activity stack.
+ // Note that we do this whether or not the showCallScreenWithDialpad()
+ // call above had any effect or not! (That call is a no-op if the
+ // phone is idle, which can happen if the current call ends while
+ // the dialpad chooser is up. In this case we can't show the
+ // InCallScreen, and there's no point staying here in the Dialer,
+ // so we just take the user back where he came from...)
+ getActivity().finish();
+ }
+
+ /**
+ * @return true if the phone is "in use", meaning that at least one line
+ * is active (ie. off hook or ringing or dialing).
+ */
+ private boolean phoneIsInUse() {
+ boolean phoneInUse = false;
+ try {
+ ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+ if (phone != null) phoneInUse = !phone.isIdle();
+ } catch (RemoteException e) {
+ Log.w(TAG, "phone.isIdle() failed", e);
+ }
+ return phoneInUse;
+ }
+
+ /**
+ * @return true if the phone is a CDMA phone type
+ */
+ private boolean phoneIsCdma() {
+ boolean isCdma = false;
+ try {
+ ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+ if (phone != null) {
+ isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "phone.getActivePhoneType() failed", e);
+ }
+ return isCdma;
+ }
+
+ /**
+ * @return true if the phone state is OFFHOOK
+ */
+ private boolean phoneIsOffhook() {
+ boolean phoneOffhook = false;
+ try {
+ ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+ if (phone != null) phoneOffhook = phone.isOffhook();
+ } catch (RemoteException e) {
+ Log.w(TAG, "phone.isOffhook() failed", e);
+ }
+ return phoneOffhook;
+ }
+
+ /**
+ * Returns true whenever any one of the options from the menu is selected.
+ * Code changes to support dialpad options
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case MENU_2S_PAUSE:
+ updateDialString(",");
+ return true;
+ case MENU_WAIT:
+ updateDialString(";");
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Updates the dial string (mDigits) after inserting a Pause character (,)
+ * or Wait character (;).
+ */
+ private void updateDialString(String newDigits) {
+ int selectionStart;
+ int selectionEnd;
+
+ // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
+ int anchor = mDigits.getSelectionStart();
+ int point = mDigits.getSelectionEnd();
+
+ selectionStart = Math.min(anchor, point);
+ selectionEnd = Math.max(anchor, point);
+
+ Editable digits = mDigits.getText();
+ if (selectionStart != -1) {
+ if (selectionStart == selectionEnd) {
+ // then there is no selection. So insert the pause at this
+ // position and update the mDigits.
+ digits.replace(selectionStart, selectionStart, newDigits);
+ } else {
+ digits.replace(selectionStart, selectionEnd, newDigits);
+ // Unselect: back to a regular cursor, just pass the character inserted.
+ mDigits.setSelection(selectionStart + 1);
+ }
+ } else {
+ int len = mDigits.length();
+ digits.replace(len, len, newDigits);
+ }
+ }
+
+ /**
+ * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
+ */
+ private void updateDialAndDeleteButtonEnabledState() {
+ final boolean digitsNotEmpty = !isDigitsEmpty();
+
+ if (mDialButton != null) {
+ // On CDMA phones, if we're already on a call, we *always*
+ // enable the Dial button (since you can press it without
+ // entering any digits to send an empty flash.)
+ if (phoneIsCdma() && phoneIsOffhook()) {
+ mDialButton.setEnabled(true);
+ } else {
+ // Common case: GSM, or CDMA but not on a call.
+ // Enable the Dial button if some digits have
+ // been entered, or if there is a last dialed number
+ // that could be redialed.
+ mDialButton.setEnabled(digitsNotEmpty ||
+ !TextUtils.isEmpty(mLastNumberDialed));
+ }
+ }
+ mDelete.setEnabled(digitsNotEmpty);
+ }
+
+ /**
+ * Check if voicemail is enabled/accessible.
+ */
+ private void initVoicemailButton() {
+ boolean hasVoicemail = false;
+ try {
+ hasVoicemail = TelephonyManager.getDefault().getVoiceMailNumber() != null;
+ } catch (SecurityException se) {
+ // Possibly no READ_PHONE_STATE privilege.
+ }
+
+ mVoicemailButton = mVoicemailDialAndDeleteRow.findViewById(R.id.voicemailButton);
+ if (hasVoicemail) {
+ mVoicemailButton.setOnClickListener(this);
+ } else {
+ mVoicemailButton.setEnabled(false);
+ }
+ }
+
+ /**
+ * This function return true if Wait menu item can be shown
+ * otherwise returns false. Assumes the passed string is non-empty
+ * and the 0th index check is not required.
+ */
+ private boolean showWait(int start, int end, String digits) {
+ if (start == end) {
+ // visible false in this case
+ if (start > digits.length()) return false;
+
+ // preceding char is ';', so visible should be false
+ if (digits.charAt(start - 1) == ';') return false;
+
+ // next char is ';', so visible should be false
+ if ((digits.length() > start) && (digits.charAt(start) == ';')) return false;
+ } else {
+ // visible false in this case
+ if (start > digits.length() || end > digits.length()) return false;
+
+ // In this case we need to just check for ';' preceding to start
+ // or next to end
+ if (digits.charAt(start - 1) == ';') return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return true if the widget with the phone number digits is empty.
+ */
+ private boolean isDigitsEmpty() {
+ return mDigits.length() == 0;
+ }
+
+ /**
+ * Starts the asyn query to get the last dialed/outgoing
+ * number. When the background query finishes, mLastNumberDialed
+ * is set to the last dialed number or an empty string if none
+ * exists yet.
+ */
+ private void queryLastOutgoingCall() {
+ mLastNumberDialed = EMPTY_NUMBER;
+ CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
+ new CallLogAsync.GetLastOutgoingCallArgs(
+ getActivity(),
+ new CallLogAsync.OnLastOutgoingCallComplete() {
+ public void lastOutgoingCall(String number) {
+ // TODO: Filter out emergency numbers if
+ // the carrier does not want redial for
+ // these.
+ mLastNumberDialed = number;
+ updateDialAndDeleteButtonEnabledState();
+ }
+ });
+ mCallLog.getLastOutgoingCall(lastCallArgs);
+ }
+
+ // Helpers for the call intents.
+ private Intent newVoicemailIntent() {
+ final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ Uri.fromParts("voicemail", EMPTY_NUMBER, null));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+
+ private Intent newFlashIntent() {
+ final Intent intent = newDialNumberIntent(EMPTY_NUMBER);
+ intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
+ return intent;
+ }
+
+ private Intent newDialNumberIntent(String number) {
+ final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ Uri.fromParts("tel", number, null));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+}
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 4effb5e..5ef2c21 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -20,11 +20,13 @@
import com.android.contacts.R;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.model.DataKind;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.EntityModifier;
+import android.accounts.Account;
import android.content.Context;
import android.content.Entity;
import android.database.Cursor;
@@ -68,6 +70,7 @@
private ViewGroup mFields;
+ private View mAccountContainer;
private ImageView mAccountIcon;
private TextView mAccountTypeTextView;
private TextView mAccountNameTextView;
@@ -136,6 +139,7 @@
mFields = (ViewGroup)findViewById(R.id.sect_fields);
+ mAccountContainer = findViewById(R.id.account);
mAccountIcon = (ImageView) findViewById(R.id.account_icon);
mAccountTypeTextView = (TextView) findViewById(R.id.account_type);
mAccountNameTextView = (TextView) findViewById(R.id.account_name);
@@ -169,22 +173,29 @@
// Make sure we have StructuredName
EntityModifier.ensureKindExists(state, type, StructuredName.CONTENT_ITEM_TYPE);
- // Fill in the header info
ValuesDelta values = state.getValues();
- String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
- CharSequence accountType = type.getDisplayLabel(mContext);
- if (TextUtils.isEmpty(accountType)) {
- accountType = mContext.getString(R.string.account_phone);
- }
- if (!TextUtils.isEmpty(accountName)) {
- mAccountNameTextView.setText(
- mContext.getString(R.string.from_account_format, accountName));
- }
- mAccountTypeTextView.setText(mContext.getString(R.string.account_type_format, accountType));
- mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
-
mRawContactId = values.getAsLong(RawContacts._ID);
+ final ArrayList<Account> accounts =
+ AccountTypeManager.getInstance(mContext).getAccounts(true);
+ if (accounts.size() > 1) {
+ // Fill in the account info
+ String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+ CharSequence accountType = type.getDisplayLabel(mContext);
+ if (TextUtils.isEmpty(accountType)) {
+ accountType = mContext.getString(R.string.account_phone);
+ }
+ if (!TextUtils.isEmpty(accountName)) {
+ mAccountNameTextView.setText(
+ mContext.getString(R.string.from_account_format, accountName));
+ }
+ mAccountTypeTextView.setText(
+ mContext.getString(R.string.account_type_format, accountType));
+ mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
+ } else {
+ mAccountContainer.setVisibility(View.GONE);
+ }
+
// Show photo editor when supported
EntityModifier.ensureKindExists(state, type, Photo.CONTENT_ITEM_TYPE);
setHasPhotoEditor((type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null));
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
new file mode 100644
index 0000000..ed69776
--- /dev/null
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.group;
+
+import com.android.contacts.R;
+import com.android.contacts.GroupMetaData;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Adapter to populate the list of groups.
+ */
+public class GroupBrowseListAdapter extends BaseAdapter {
+
+ private LayoutInflater mLayoutInflater;
+ private List<GroupMetaData> mGroupList;
+
+ public GroupBrowseListAdapter(Context context, List<GroupMetaData> groupList) {
+ mLayoutInflater = LayoutInflater.from(context);
+ mGroupList = groupList;
+ }
+
+ @Override
+ public int getCount() {
+ return mGroupList.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getItem(position).getGroupId();
+ }
+
+ @Override
+ public GroupMetaData getItem(int position) {
+ return mGroupList.get(position);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mLayoutInflater.inflate(R.layout.group_browse_list_item, parent, false);
+ }
+ GroupMetaData group = getItem(position);
+ ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+ TextView label = (TextView) convertView.findViewById(R.id.label);
+ TextView account = (TextView) convertView.findViewById(R.id.account);
+ icon.setImageResource(R.drawable.ic_menu_display_all_holo_light);
+ label.setText(group.getTitle());
+ account.setText(group.getAccountName());
+ return convertView;
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
new file mode 100644
index 0000000..978ce13
--- /dev/null
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.group;
+
+import com.android.contacts.GroupMetaData;
+import com.android.contacts.GroupMetaDataLoader;
+import com.android.contacts.R;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment to display the list of groups.
+ */
+public class GroupBrowseListFragment extends Fragment
+ implements OnFocusChangeListener, OnTouchListener {
+
+ private static final String TAG = "GroupBrowseListFragment";
+
+ private static final int LOADER_GROUPS = 1;
+
+ private Context mContext;
+ private Cursor mGroupListCursor;
+ private List<GroupMetaData> mGroupList = new ArrayList<GroupMetaData>();
+
+ private View mRootView;
+ private ListView mListView;
+ private View mEmptyView;
+
+ public GroupBrowseListFragment() {
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mRootView = inflater.inflate(R.layout.group_browse_list_fragment, null);
+ mListView = (ListView) mRootView.findViewById(R.id.list);
+ mListView.setOnFocusChangeListener(this);
+ mListView.setOnTouchListener(this);
+ mEmptyView = mRootView.findViewById(R.id.empty);
+ return mRootView;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mContext = activity;
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mContext = null;
+ }
+
+ @Override
+ public void onStart() {
+ getLoaderManager().initLoader(LOADER_GROUPS, null, mGroupLoaderListener);
+ super.onStart();
+ }
+
+ /**
+ * The listener for the group meta data loader.
+ */
+ private final LoaderManager.LoaderCallbacks<Cursor> mGroupLoaderListener =
+ new LoaderCallbacks<Cursor>() {
+
+ @Override
+ public CursorLoader onCreateLoader(int id, Bundle args) {
+ return new GroupMetaDataLoader(mContext);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mGroupListCursor = data;
+ bindGroupList();
+ }
+
+ public void onLoaderReset(Loader<Cursor> loader) {
+ }
+ };
+
+ private void bindGroupList() {
+ if (mGroupListCursor == null) {
+ return;
+ }
+ mGroupList.clear();
+ mGroupListCursor.moveToPosition(-1);
+ while (mGroupListCursor.moveToNext()) {
+ String accountName = mGroupListCursor.getString(GroupMetaDataLoader.ACCOUNT_NAME);
+ String accountType = mGroupListCursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+ long groupId = mGroupListCursor.getLong(GroupMetaDataLoader.GROUP_ID);
+ String title = mGroupListCursor.getString(GroupMetaDataLoader.TITLE);
+ boolean defaultGroup = mGroupListCursor.isNull(GroupMetaDataLoader.AUTO_ADD)
+ ? false
+ : mGroupListCursor.getInt(GroupMetaDataLoader.AUTO_ADD) != 0;
+ boolean favorites = mGroupListCursor.isNull(GroupMetaDataLoader.FAVORITES)
+ ? false
+ : mGroupListCursor.getInt(GroupMetaDataLoader.FAVORITES) != 0;
+
+ // TODO: Separate groups according to account name and type.
+ mGroupList.add(new GroupMetaData(
+ accountName, accountType, groupId, title, defaultGroup, favorites));
+ }
+
+ mListView.setAdapter(new GroupBrowseListAdapter(mContext, mGroupList));
+ mListView.setEmptyView(mEmptyView);
+ }
+
+ private void hideSoftKeyboard() {
+ if (mContext == null) {
+ return;
+ }
+ // Hide soft keyboard, if visible
+ InputMethodManager inputMethodManager = (InputMethodManager)
+ mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.hideSoftInputFromWindow(mListView.getWindowToken(), 0);
+ }
+
+ /**
+ * Dismisses the soft keyboard when the list takes focus.
+ */
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (view == mListView && hasFocus) {
+ hideSoftKeyboard();
+ }
+ }
+
+ /**
+ * Dismisses the soft keyboard when the list is touched.
+ */
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ if (view == mListView) {
+ hideSoftKeyboard();
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index c85f582..fcced62 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -821,6 +821,7 @@
}
}
+ @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
hideSoftKeyboard();
@@ -840,6 +841,7 @@
/**
* Dismisses the soft keyboard when the list takes focus.
*/
+ @Override
public void onFocusChange(View view, boolean hasFocus) {
if (view == mListView && hasFocus) {
hideSoftKeyboard();
@@ -849,6 +851,7 @@
/**
* Dismisses the soft keyboard when the list is touched.
*/
+ @Override
public boolean onTouch(View view, MotionEvent event) {
if (view == mListView) {
hideSoftKeyboard();
diff --git a/src/com/android/contacts/util/AccountsListAdapter.java b/src/com/android/contacts/util/AccountsListAdapter.java
index 8dbfc8d..97a9f84 100644
--- a/src/com/android/contacts/util/AccountsListAdapter.java
+++ b/src/com/android/contacts/util/AccountsListAdapter.java
@@ -60,7 +60,9 @@
final AccountType accountType = mAccountTypes.getAccountType(account.type);
text1.setText(account.name);
- text2.setText(accountType.getDisplayLabel(mContext));
+ if (text2 != null) {
+ text2.setText(accountType.getDisplayLabel(mContext));
+ }
icon.setImageDrawable(accountType.getDisplayIcon(mContext));
return resultView;