Merge "Fixed presence icons for ContactTiles and ContactListItem."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 95edf0f..aeec2b2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -53,17 +53,6 @@
android:hardwareAccelerated="true"
>
- <!-- A virtual 12 key dialer -->
- <activity android:name=".activities.DialpadActivity"
- android:launchMode="singleTop"
- >
- <intent-filter>
- <action android:name="com.android.phone.action.TOUCH_DIALER" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.TAB" />
- </intent-filter>
- </activity>
-
<!-- A list of recent calls -->
<activity android:name=".activities.CallLogActivity"
android:label="@string/recentCallsIconLabel"
@@ -181,6 +170,13 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
+ <!-- This was never intended to be public, but is here for backward
+ compatibility. Use Intent.ACTION_DIAL instead. -->
+ <intent-filter>
+ <action android:name="com.android.phone.action.TOUCH_DIALER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.TAB" />
+ </intent-filter>
</activity>
<!-- The main Contacts activity with the contact list, favorites, and groups. -->
diff --git a/res/layout-sw580dp/contact_detail_fragment.xml b/res/layout-sw580dp/contact_detail_fragment.xml
index c9dad2a..f05dc56 100644
--- a/res/layout-sw580dp/contact_detail_fragment.xml
+++ b/res/layout-sw580dp/contact_detail_fragment.xml
@@ -30,8 +30,9 @@
<!-- Real list -->
<ListView android:id="@android:id/list"
+ android:layout_weight="1"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="0dip"
android:cacheColorHint="#00000000"
android:divider="@null"
/>
diff --git a/res/layout/contact_tile_frequent.xml b/res/layout/contact_tile_frequent.xml
index 2e79ba6..52b7126 100644
--- a/res/layout/contact_tile_frequent.xml
+++ b/res/layout/contact_tile_frequent.xml
@@ -17,7 +17,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
class="com.android.contacts.list.ContactTileView"
android:focusable="true"
- android:background="@null"
+ android:background="?android:attr/selectableItemBackground"
+ android:nextFocusRight="@+id/contact_tile_quick"
android:paddingRight="16dip"
android:paddingLeft="16dip" >
@@ -30,6 +31,7 @@
android:layout_width="64dip"
android:layout_height="64dip"
android:scaleType="centerCrop"
+ android:focusable="true"
android:layout_alignParentRight="true" />
<LinearLayout
diff --git a/res/layout/contact_tile_frequent_phone.xml b/res/layout/contact_tile_frequent_phone.xml
index 563dea0..7d24cef 100644
--- a/res/layout/contact_tile_frequent_phone.xml
+++ b/res/layout/contact_tile_frequent_phone.xml
@@ -14,10 +14,12 @@
limitations under the License.
-->
<view
+ android:id="@+id/contact_tile_frequent_phone"
xmlns:android="http://schemas.android.com/apk/res/android"
class="com.android.contacts.list.ContactTileView"
android:focusable="true"
- android:background="@null"
+ android:background="?android:attr/selectableItemBackground"
+ android:nextFocusLeft="@+id/contact_tile_quick"
android:paddingRight="16dip"
android:paddingLeft="16dip" >
@@ -26,10 +28,12 @@
android:layout_height="match_parent" >
<QuickContactBadge
- android:id="@+id/contact_tile_quick"
+ android:id="@id/contact_tile_quick"
+ android:nextFocusRight="@id/contact_tile_frequent_phone"
android:layout_width="64dip"
android:layout_height="64dip"
android:scaleType="centerCrop"
+ android:focusable="true"
android:layout_alignParentLeft="true" />
<TextView
diff --git a/res/layout/contact_tile_starred.xml b/res/layout/contact_tile_starred.xml
index 5fdfe65..757abea 100644
--- a/res/layout/contact_tile_starred.xml
+++ b/res/layout/contact_tile_starred.xml
@@ -70,6 +70,7 @@
android:id="@+id/contact_tile_push_state"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:focusable="true"
android:background="?android:attr/selectableItemBackground" />
</RelativeLayout>
diff --git a/res/layout/contact_tile_starred_quick_contact.xml b/res/layout/contact_tile_starred_quick_contact.xml
index 9bb4abe..3f072d1 100644
--- a/res/layout/contact_tile_starred_quick_contact.xml
+++ b/res/layout/contact_tile_starred_quick_contact.xml
@@ -69,6 +69,7 @@
android:id="@+id/contact_tile_quick"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:focusable="true"
android:background="@null" />
</RelativeLayout>
diff --git a/res/layout/contact_tile_starred_secondary_target.xml b/res/layout/contact_tile_starred_secondary_target.xml
index e72575e..6543dc1 100644
--- a/res/layout/contact_tile_starred_secondary_target.xml
+++ b/res/layout/contact_tile_starred_secondary_target.xml
@@ -51,10 +51,12 @@
android:id="@+id/contact_tile_push_state"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:focusable="true"
+ android:nextFocusRight="@+id/contact_tile_secondary_button"
android:background="?android:attr/selectableItemBackground" />
<ImageButton
- android:id="@+id/contact_tile_secondary_button"
+ android:id="@id/contact_tile_secondary_button"
android:src="@drawable/ic_contacts_holo_dark"
android:background="?android:attr/selectableItemBackground"
android:layout_height="@dimen/contact_tile_shadowbox_height"
diff --git a/res/layout/dialtacts_custom_action_bar.xml b/res/layout/dialtacts_custom_action_bar.xml
new file mode 100644
index 0000000..2be66f7
--- /dev/null
+++ b/res/layout/dialtacts_custom_action_bar.xml
@@ -0,0 +1,41 @@
+<?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.
+-->
+
+<!-- Dimensions are set at runtime in ActionBarAdapter -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:orientation="horizontal">
+
+ <SearchView
+ android:id="@+id/search_view"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:iconifiedByDefault="false" />
+
+ <ImageButton
+ android:id="@+id/search_option"
+ android:layout_width="wrap_content"
+ android:paddingRight="8dip"
+ android:layout_height="match_parent"
+ android:layout_alignParentRight="true"
+ android:src="@drawable/ic_menu_overflow"
+ android:background="@android:color/transparent"
+ android:visibility="gone" />
+
+</LinearLayout>
diff --git a/res/menu/dialpad_options.xml b/res/menu/dialpad_options.xml
index 77da9cb..4dc62a8 100644
--- a/res/menu/dialpad_options.xml
+++ b/res/menu/dialpad_options.xml
@@ -30,4 +30,10 @@
android:icon="@drawable/ic_menu_wait"
android:title="@string/add_wait"
android:showAsAction="withText" />
+
+ <item
+ android:id="@+id/menu_call_settings_dialpad"
+ android:title="@string/call_settings"
+ android:icon="@drawable/ic_menu_settings_holo_light"
+ android:showAsAction="withText" />
</menu>
diff --git a/res/menu/dialtacts_search_options.xml b/res/menu/dialtacts_search_options.xml
new file mode 100644
index 0000000..f2e0c67
--- /dev/null
+++ b/res/menu/dialtacts_search_options.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<!-- Used with DialtactsActivity's search mode. -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/filter_option"
+ android:title="@string/menu_contacts_filter"
+ android:showAsAction="withText" />
+</menu>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2f74c32..a67aa6b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -256,6 +256,6 @@
<dimen name="directory_header_height">24dip</dimen>
<!-- Vertical and horizontal padding in between contact tiles -->
- <dimen name="contact_tile_divider_padding">2dip</dimen>
+ <dimen name="contact_tile_divider_padding">1dip</dimen>
</resources>
diff --git a/src/com/android/contacts/activities/DialpadActivity.java b/src/com/android/contacts/activities/DialpadActivity.java
deleted file mode 100644
index 1221068..0000000
--- a/src/com/android/contacts/activities/DialpadActivity.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2007 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.ContactsSearchManager;
-import com.android.contacts.R;
-import com.android.contacts.dialpad.DialpadFragment;
-
-import android.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.ViewConfiguration;
-import android.view.Window;
-import android.view.inputmethod.InputMethodManager;
-
-/**
- * Activity that displays a twelve-key phone dialpad.
- * This is just a simple container around DialpadFragment.
- * @see DialpadFragment
- */
-public class DialpadActivity extends Activity {
- private static final String TAG = "DialpadActivity";
-
- private DialpadFragment mFragment;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- 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);
- }
-
- mFragment = (DialpadFragment) getFragmentManager().findFragmentById(
- R.id.dialpad_fragment);
- }
-
- @Override
- protected void onNewIntent(Intent newIntent) {
- setIntent(newIntent);
- mFragment.configureScreenFromIntent(newIntent);
- }
-
- public DialpadFragment getFragment() {
- return mFragment;
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- if (hasFocus) {
- // Hide soft keyboard, if visible (it's fugly over button dialer).
- // The only known case where this will be true is when launching the dialer with
- // ACTION_DIAL via a soft keyboard. we dismiss it here because we don't
- // have a window token yet in onCreate / onNewIntent
- InputMethodManager inputMethodManager = (InputMethodManager)
- getSystemService(Context.INPUT_METHOD_SERVICE);
- inputMethodManager.hideSoftInputFromWindow(
- mFragment.getDigitsWidget().getWindowToken(), 0);
- }
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_CALL: {
- long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
- if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
- // Launch voice dialer
- Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "Failed to launch voice dialer: " + e);
- }
- }
- return true;
- }
- case KeyEvent.KEYCODE_1: {
- long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
- if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
- // Long press detected, call voice mail
- mFragment.callVoicemail();
- }
- return true;
- }
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_CALL: {
- mFragment.dialButtonPressed();
- return true;
- }
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
- public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
- boolean globalSearch) {
- if (globalSearch) {
- super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
- } else {
- ContactsSearchManager.startSearch(this, initialQuery);
- }
- }
-}
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 6200a69..b6dfbfa 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -57,8 +57,10 @@
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
+import android.view.View.OnClickListener;
+import android.view.ViewConfiguration;
import android.view.inputmethod.InputMethodManager;
+import android.widget.PopupMenu;
import android.widget.SearchView;
import android.widget.SearchView.OnCloseListener;
import android.widget.SearchView.OnQueryTextListener;
@@ -78,6 +80,11 @@
private static final String CALL_SETTINGS_CLASS_NAME =
"com.android.phone.CallFeaturesSetting";
+ /**
+ * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
+ */
+ private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
+
/** Used both by {@link ActionBar} and {@link ViewPagerAdapter} */
private static final int TAB_INDEX_DIALER = 0;
private static final int TAB_INDEX_CALL_LOG = 1;
@@ -238,6 +245,18 @@
private boolean mInSearchUi;
private SearchView mSearchView;
+ private final OnClickListener mFilterOptionClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ final PopupMenu popupMenu = new PopupMenu(DialtactsActivity.this, view);
+ final Menu menu = popupMenu.getMenu();
+ popupMenu.inflate(R.menu.dialtacts_search_options);
+ final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
+ filterOptionMenuItem.setOnMenuItemClickListener(mFilterOptionsMenuItemClickListener);
+ popupMenu.show();
+ }
+ };
+
/**
* The index of the Fragment (or, the tab) that has last been manually selected.
* This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
@@ -358,6 +377,8 @@
mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
mViewPager.setOnPageChangeListener(mPageChangeListener);
+ prepareSearchView();
+
// Setup the ActionBar tabs (the order matches the tab-index contants TAB_INDEX_*)
setupDialer();
setupCallLog();
@@ -383,6 +404,33 @@
}
}
+ private void prepareSearchView() {
+ final View searchViewLayout =
+ getLayoutInflater().inflate(R.layout.dialtacts_custom_action_bar, null);
+ mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
+ mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
+ mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
+ // Since we're using a custom layout for showing SearchView instead of letting the
+ // search menu icon do that job, we need to manually configure the View so it looks
+ // "shown via search menu".
+ // - it should be iconified by default
+ // - it should not be iconified at this time
+ // See also comments for onActionViewExpanded()/onActionViewCollapsed()
+ mSearchView.setIconifiedByDefault(true);
+ mSearchView.setQueryHint(getString(R.string.hint_findContacts));
+ mSearchView.setIconified(false);
+
+ if (!ViewConfiguration.get(this).hasPermanentMenuKey()) {
+ // Filter option menu should be shown on the right side of SearchView.
+ final View filterOptionView = searchViewLayout.findViewById(R.id.search_option);
+ filterOptionView.setVisibility(View.VISIBLE);
+ filterOptionView.setOnClickListener(mFilterOptionClickListener);
+ }
+
+ getActionBar().setCustomView(searchViewLayout,
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ }
+
@Override
public void onAttachFragment(Fragment fragment) {
// This method can be called before onCreate(), at which point we cannot rely on ViewPager.
@@ -558,7 +606,7 @@
/** Returns true if the given intent contains a phone number to populate the dialer with */
private boolean isDialIntent(Intent intent) {
final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action)) {
+ if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
return true;
}
if (Intent.ACTION_VIEW.equals(action)) {
@@ -648,20 +696,35 @@
Tab tab = getActionBar().getSelectedTab();
if (mInSearchUi) {
searchMenuItem.setVisible(false);
- filterOptionMenuItem.setVisible(true);
- filterOptionMenuItem.setOnMenuItemClickListener(
- mFilterOptionsMenuItemClickListener);
+ if (ViewConfiguration.get(this).hasPermanentMenuKey()) {
+ filterOptionMenuItem.setVisible(true);
+ filterOptionMenuItem.setOnMenuItemClickListener(
+ mFilterOptionsMenuItemClickListener);
+ } else {
+ // Filter option menu should be not be shown as a overflow menu.
+ filterOptionMenuItem.setVisible(false);
+ }
callSettingsMenuItem.setVisible(false);
} else {
+ final boolean showCallSettingsMenu;
if (tab != null && tab.getPosition() == TAB_INDEX_DIALER) {
searchMenuItem.setVisible(false);
+ // When permanent menu key is _not_ available, the call settings menu should be
+ // available via DialpadFragment.
+ showCallSettingsMenu = ViewConfiguration.get(this).hasPermanentMenuKey();
} else {
searchMenuItem.setVisible(true);
searchMenuItem.setOnMenuItemClickListener(mSearchMenuItemClickListener);
+ showCallSettingsMenu = true;
}
filterOptionMenuItem.setVisible(false);
- callSettingsMenuItem.setVisible(true);
- callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent());
+
+ if (showCallSettingsMenu) {
+ callSettingsMenuItem.setVisible(true);
+ callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent());
+ } else {
+ callSettingsMenuItem.setVisible(false);
+ }
}
return true;
@@ -690,46 +753,7 @@
mLastManuallySelectedFragment = tab.getPosition();
}
- // Instantiate or reset SearchView in ActionBar.
- if (mSearchView == null) {
- final View searchViewLayout =
- getLayoutInflater().inflate(R.layout.custom_action_bar, null);
- mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
- mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
- mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
- // Since we're using a custom layout for showing SearchView instead of letting the
- // search menu icon do that job, we need to manually configure the View so it looks
- // "shown via search menu".
- // - it should be iconified by default
- // - it should not be iconified at this time
- // See also comments for onActionViewExpanded()/onActionViewCollapsed()
- mSearchView.setIconifiedByDefault(true);
- mSearchView.setQueryHint(getString(R.string.hint_findContacts));
- mSearchView.setIconified(false);
- mSearchView.requestFocus();
- // Show soft keyboard when SearchView has a focus. Need to delay the request in order
- // to let InputMethodManager handle it correctly.
- mSearchView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
- @Override
- public void onViewDetachedFromWindow(View v) {
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- if (mSearchView.hasFocus()) {
- mSearchView.postDelayed(new Runnable() {
- public void run() {
- showInputMethod(mSearchView.findFocus());
- }
- }, 0);
- }
- }
- });
- actionBar.setCustomView(searchViewLayout,
- new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- } else {
- mSearchView.setQuery(null, true);
- }
+ mSearchView.setQuery(null, true);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
@@ -748,6 +772,9 @@
// layout instead of asking the search menu item to take care of SearchView.
mSearchView.onActionViewExpanded();
mInSearchUi = true;
+
+ // Clear focus and suppress keyboard show-up.
+ mSearchView.clearFocus();
}
private void showInputMethod(View view) {
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 1da1c97..68e9cbb 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -156,6 +156,7 @@
/** ViewPager for swipe, used only on the phone (i.e. one-pane mode) */
private ViewPager mTabPager;
private TabPagerAdapter mTabPagerAdapter;
+ private final TabPagerListener mTabPagerListener = new TabPagerListener();
private ContactDetailLayoutController mContactDetailLayoutController;
@@ -315,7 +316,7 @@
mTabPager = getView(R.id.tab_pager);
mTabPagerAdapter = new TabPagerAdapter();
mTabPager.setAdapter(mTabPagerAdapter);
- mTabPager.setOnPageChangeListener(new TabPagerListener());
+ mTabPager.setOnPageChangeListener(mTabPagerListener);
final String FAVORITE_TAG = "tab-pager-favorite";
final String ALL_TAG = "tab-pager-all";
@@ -450,6 +451,9 @@
// Re-register the listener, which may have been cleared when onSaveInstanceState was
// called. See also: onSaveInstanceState
mActionBarAdapter.setListener(this);
+ if (mTabPager != null) {
+ mTabPager.setOnPageChangeListener(mTabPagerListener);
+ }
// Current tab may have changed since the last onSaveInstanceState(). Make sure
// the actual contents match the tab.
updateFragmentsVisibility();
@@ -1527,6 +1531,9 @@
// in order to avoid doing fragment transactions after it.
// TODO Figure out a better way to deal with the issue.
mActionBarAdapter.setListener(null);
+ if (mTabPager != null) {
+ mTabPager.setOnPageChangeListener(null);
+ }
}
@Override
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index 62ba95e..93bd3e9 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -571,10 +571,20 @@
}
private void setupMenuItems(Menu menu) {
+ final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings_dialpad);
final MenuItem addToContactMenuItem = menu.findItem(R.id.menu_add_contacts);
final MenuItem twoSecPauseMenuItem = menu.findItem(R.id.menu_2s_pause);
final MenuItem waitMenuItem = menu.findItem(R.id.menu_add_wait);
+ final Activity activity = getActivity();
+ if (activity != null && ViewConfiguration.get(activity).hasPermanentMenuKey()) {
+ // Call settings should be available via its parent Activity.
+ callSettingsMenuItem.setVisible(false);
+ } else {
+ callSettingsMenuItem.setVisible(true);
+ callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent());
+ }
+
// We show "add to contacts", "2sec pause", and "add wait" menus only when the user is
// seeing usual dialpads and has typed at least one digit.
// We never show a menu if the "choose dialpad" UI is up.
diff --git a/src/com/android/contacts/editor/ExternalRawContactEditorView.java b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
index cabf639..eb496f2 100644
--- a/src/com/android/contacts/editor/ExternalRawContactEditorView.java
+++ b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
@@ -168,7 +168,8 @@
// Name
primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
- mName.setText(primary.getAsString(StructuredName.DISPLAY_NAME));
+ mName.setText(primary != null ? primary.getAsString(StructuredName.DISPLAY_NAME) :
+ mContext.getString(R.string.missing_name));
if (type.readOnly) {
mAccountContainer.setOnClickListener(new OnClickListener() {
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 9ee5fb3..b7434b1 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -520,7 +520,6 @@
} else {
contactTile = (ContactTileView) getChildAt(childIndex);
}
- contactTile.setClickable(entry != null);
contactTile.loadFromContact(entry);
switch (mItemViewType) {
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 60a7357..2d821e6 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -338,6 +338,17 @@
// Skip external account types that couldn't be initialized.
continue;
}
+ if (!accountType.hasContactsMetadata()) {
+ Log.w(TAG, "Skipping extension package " + extensionPackage + " because"
+ + " it doesn't have the CONTACTS_STRUCTURE metadata");
+ continue;
+ }
+ if (TextUtils.isEmpty(accountType.accountType)) {
+ Log.w(TAG, "Skipping extension package " + extensionPackage + " because"
+ + " the CONTACTS_STRUCTURE metadata doesn't have the accountType"
+ + " attribute");
+ continue;
+ }
Log.d(TAG, "Registering extension package account type="
+ accountType.accountType + ", dataSet=" + accountType.dataSet
+ ", packageName=" + extensionPackage);
diff --git a/src/com/android/contacts/model/AccountTypeWithDataSet.java b/src/com/android/contacts/model/AccountTypeWithDataSet.java
index d5cdbdd..f1b2344 100644
--- a/src/com/android/contacts/model/AccountTypeWithDataSet.java
+++ b/src/com/android/contacts/model/AccountTypeWithDataSet.java
@@ -25,16 +25,14 @@
* Encapsulates an "account type" string and a "data set" string.
*/
public class AccountTypeWithDataSet {
- /** account type will never be null. */
+ /** account type. Can be null for fallback type. */
public final String accountType;
/** dataSet may be null, but never be "". */
public final String dataSet;
private AccountTypeWithDataSet(String accountType, String dataSet) {
- if (accountType == null) throw new NullPointerException();
-
- this.accountType = accountType;
+ this.accountType = TextUtils.isEmpty(accountType) ? null : accountType;
this.dataSet = TextUtils.isEmpty(dataSet) ? null : dataSet;
}
@@ -53,7 +51,8 @@
@Override
public int hashCode() {
- return Objects.hashCode(accountType) ^ (dataSet == null ? 0 : Objects.hashCode(dataSet));
+ return (accountType == null ? 0 : accountType.hashCode())
+ ^ (dataSet == null ? 0 : dataSet.hashCode());
}
@Override
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index 791f0ae..b6649c9 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -22,11 +22,9 @@
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -46,7 +44,6 @@
public class ExternalAccountType extends BaseAccountType {
private static final String TAG = "ExternalAccountType";
- private static final String ACTION_SYNC_ADAPTER = "android.content.SyncAdapter";
private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
private static final String TAG_CONTACTS_SOURCE_LEGACY = "ContactsSource";
@@ -85,6 +82,7 @@
private String mAccountTypeLabelAttribute;
private String mAccountTypeIconAttribute;
private boolean mInitSuccessful;
+ private boolean mHasContactsMetadata;
public ExternalAccountType(Context context, String resPackageName) {
this.resPackageName = resPackageName;
@@ -137,6 +135,13 @@
return mInitSuccessful;
}
+ /**
+ * Whether this account type has the android.provider.CONTACTS_STRUCTURE metadata xml.
+ */
+ public boolean hasContactsMetadata() {
+ return mHasContactsMetadata;
+ }
+
@Override
public String getEditContactActivityClassName() {
return mEditContactActivityClassName;
@@ -207,6 +212,8 @@
+ TAG_CONTACTS_ACCOUNT_TYPE + ", not " + rootTag);
}
+ mHasContactsMetadata = true;
+
int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; i++) {
String attr = parser.getAttributeName(i);
diff --git a/src/com/android/contacts/util/ContactBadgeUtil.java b/src/com/android/contacts/util/ContactBadgeUtil.java
index 806264d..a89177a 100644
--- a/src/com/android/contacts/util/ContactBadgeUtil.java
+++ b/src/com/android/contacts/util/ContactBadgeUtil.java
@@ -57,10 +57,15 @@
final String statusLabelRes = streamItem.getLabelRes();
final String statusResPackage = streamItem.getResPackage();
+
+ // Package name used for resources.getIdentifier()
+ String identiferPackage = statusResPackage;
if (statusLabelRes != null) {
Resources resources;
if (TextUtils.isEmpty(statusResPackage)) {
resources = context.getResources();
+ // In this case, we're using the framework resources.
+ identiferPackage = "android";
} else {
PackageManager pm = context.getPackageManager();
try {
@@ -74,7 +79,7 @@
if (resources != null) {
final int resId = resources.getIdentifier(statusLabelRes, "string",
- statusResPackage);
+ identiferPackage);
if (resId == 0) {
Log.w(TAG, "Contact status update resource not found: " + statusLabelRes +
" in " + statusResPackage);