Merge "Performing fragment transaction with delay"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1dc0235..f44edf3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -65,6 +65,57 @@
</intent-filter>
</activity>
+ <!-- Intercept Dialer Intents for devices without a phone.
+ This activity should have the same intent filters as the DialtactsActivity,
+ so that its capturing the same events. Omit android.intent.category.LAUNCHER, because we
+ don't want this to show up in the Launcher. The priorities of the intent-filters
+ are set lower, so that the user does not see a disambig dialog -->
+ <activity
+ android:name=".activities.NonPhoneActivity"
+ android:theme="@style/NonPhoneActivityTheme"
+ >
+ <intent-filter android:priority="-1">
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:mimeType="vnd.android.cursor.item/phone" />
+ <data android:mimeType="vnd.android.cursor.item/person" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="voicemail" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.intent.action.VIEW" />
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="tel" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:mimeType="vnd.android.cursor.dir/calls" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.intent.action.CALL_BUTTON" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ </intent-filter>
+ </activity>
+
<!-- Tab container for all tabs -->
<activity android:name="DialtactsActivity"
android:label="@string/launcherDialer"
@@ -73,7 +124,7 @@
android:clearTaskOnLaunch="true"
android:icon="@drawable/ic_launcher_phone"
android:screenOrientation="nosensor"
- android:enabled="@bool/dialerEnabled"
+ android:enabled="@*android:bool/config_voice_capable"
>
<intent-filter>
<action android:name="android.intent.action.DIAL" />
@@ -139,32 +190,6 @@
</intent-filter>
</activity>
- <!-- Main launch Intent to open the Contacts app. This will open the app in its last manual
- state. This is the state that has been explicitly set by the user (e.g. by clicking a tab).
- States configured via other Intents (e.g. CallLog after Call) are not considered manual
- state. At the moment, the Intent always goes to the DialtactsActivity, but this might later
- be changed to also include sub-activities like Edit or View if they were left open -->
-
- <activity-alias android:name="ContactsLaunchActivity"
- android:targetActivity="DialtactsActivity"
- >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- </intent-filter>
- </activity-alias>
-
- <!-- An empty activity that presents the DialtactActivity's Favorites tab -->
- <activity-alias android:name="DialtactsFavoritesEntryActivity"
- android:targetActivity="DialtactsActivity"
- >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity-alias>
-
<!-- The actual list of contacts -->
<activity android:name=".activities.ContactBrowserActivity"
android:label="@string/contactsList"
@@ -362,7 +387,7 @@
android:targetActivity=".activities.ContactSelectionActivity"
android:label="@string/shortcutDialContact"
android:icon="@drawable/ic_launcher_shortcut_directdial"
- android:enabled="@bool/dialerEnabled">
+ android:enabled="@*android:bool/config_voice_capable">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
@@ -376,7 +401,7 @@
android:targetActivity=".activities.ContactSelectionActivity"
android:label="@string/shortcutMessageContact"
android:icon="@drawable/ic_launcher_shortcut_directmessage"
- android:enabled="@bool/dialerEnabled">
+ android:enabled="@*android:bool/config_voice_capable">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
diff --git a/res/values-xlarge/styles.xml b/res/values-xlarge/styles.xml
index 0972468..f6ffa33 100644
--- a/res/values-xlarge/styles.xml
+++ b/res/values-xlarge/styles.xml
@@ -98,4 +98,7 @@
<style name="DirectoryHeader" parent="ContactBrowserTheme">
<item name="android:background">@drawable/directory_bg_holo</item>
</style>
+
+ <style name="NonPhoneActivityTheme" parent="@android:Theme.Holo.Light.Dialog">
+ </style>
</resources>
diff --git a/res/values/donottranslate_config.xml b/res/values/donottranslate_config.xml
index e41e153..cad2a63 100644
--- a/res/values/donottranslate_config.xml
+++ b/res/values/donottranslate_config.xml
@@ -18,9 +18,6 @@
-->
<resources>
- <!-- Indicates whether the dialer activity is enabled in the build -->
- <bool name="dialerEnabled">true</bool>
-
<!-- Flag indicating whether Contacts app is allowed to import contacts from SDCard -->
<bool name="config_allow_import_from_sdcard">true</bool>
<!-- If true, all vcard files are imported from SDCard without asking a user.
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 42922fa..b14a95e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1370,8 +1370,16 @@
<!-- Text used to show a organization that has both a company and title. This is used in the Detail-View
of a Contact. This is mostly about the formatting of the two elements, so it should be kept small [CHAR LIMIT=79] -->
<string name="organization_company_and_title"><xliff:g id="company" example="Technical Program Manager">%2$s</xliff:g>, <xliff:g id="company" example="Google Inc.">%1$s</xliff:g></string>
-
+
<!-- Query hint displayed inside the search field [CHAR LIMIT=64] -->
<string name="hint_findContacts">Find contacts</string>
-
+
+ <!-- Title shown for the phone number when the number tries to call on a device that it not a phone [CHAR LIMIT=30] -->
+ <string name="non_phone_caption">Phone number</string>
+
+ <!-- Button to add a phone number to contacts [CHAR LIMIT=25] -->
+ <string name="non_phone_add_to_contacts">Add to contacts</string>
+
+ <!-- Button to close without add a phone number to contacts [CHAR LIMIT=25] -->
+ <string name="non_phone_close">Close</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 5f64e27..7924bc2 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -211,4 +211,7 @@
<style name="DirectoryHeader" parent="ContactBrowserTheme">
<item name="android:background">@drawable/directory_bg</item>
</style>
+
+ <style name="NonPhoneActivityTheme" parent="@android:Theme.Dialog">
+ </style>
</resources>
diff --git a/src/com/android/contacts/DialtactsActivity.java b/src/com/android/contacts/DialtactsActivity.java
index ce8d3f4..dc69194 100644
--- a/src/com/android/contacts/DialtactsActivity.java
+++ b/src/com/android/contacts/DialtactsActivity.java
@@ -43,12 +43,6 @@
*/
public class DialtactsActivity extends TabActivity implements TabHost.OnTabChangeListener {
private static final String TAG = "Dailtacts";
- private static final String FAVORITES_ENTRY_COMPONENT =
- "com.android.contacts.DialtactsFavoritesEntryActivity";
-
- /** Opens the Contacts app in the state the user has last set it to */
- private static final String CONTACTS_LAUNCH_ACTIVITY =
- "com.android.contacts.ContactsLaunchActivity";
private static final int TAB_INDEX_DIALER = 0;
private static final int TAB_INDEX_CALL_LOG = 1;
@@ -231,8 +225,16 @@
// Choose the tab based on the inbound intent
if (intent.getBooleanExtra(ContactsFrontDoor.EXTRA_FRONT_DOOR, false)) {
- // Launched through the contacts front door, set the proper contacts tab
- setContactsTab();
+ // Launched through the contacts front door, set the proper contacts tab (sticky
+ // between favorites and contacts)
+ SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
+ boolean favoritesAsContacts = prefs.getBoolean(PREF_FAVORITES_AS_CONTACTS,
+ PREF_FAVORITES_AS_CONTACTS_DEFAULT);
+ if (favoritesAsContacts) {
+ mTabHost.setCurrentTab(TAB_INDEX_FAVORITES);
+ } else {
+ mTabHost.setCurrentTab(TAB_INDEX_CONTACTS);
+ }
} else {
// Not launched through the front door, look at the component to determine the tab
String componentName = intent.getComponent().getClassName();
@@ -242,12 +244,8 @@
} else {
mTabHost.setCurrentTab(TAB_INDEX_DIALER);
}
- } else if (FAVORITES_ENTRY_COMPONENT.equals(componentName)) {
- mTabHost.setCurrentTab(TAB_INDEX_FAVORITES);
- } else if (CONTACTS_LAUNCH_ACTIVITY.equals(componentName)) {
- mTabHost.setCurrentTab(mLastManuallySelectedTab);
} else {
- setContactsTab();
+ mTabHost.setCurrentTab(mLastManuallySelectedTab);
}
}
@@ -259,17 +257,6 @@
intent.putExtra(EXTRA_IGNORE_STATE, false);
}
- private void setContactsTab() {
- SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
- boolean favoritesAsContacts = prefs.getBoolean(PREF_FAVORITES_AS_CONTACTS,
- PREF_FAVORITES_AS_CONTACTS_DEFAULT);
- if (favoritesAsContacts) {
- mTabHost.setCurrentTab(TAB_INDEX_FAVORITES);
- } else {
- mTabHost.setCurrentTab(TAB_INDEX_CONTACTS);
- }
- }
-
@Override
public void onNewIntent(Intent newIntent) {
setIntent(newIntent);
diff --git a/src/com/android/contacts/activities/NonPhoneActivity.java b/src/com/android/contacts/activities/NonPhoneActivity.java
new file mode 100644
index 0000000..d963cd5
--- /dev/null
+++ b/src/com/android/contacts/activities/NonPhoneActivity.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+package com.android.contacts.activities;
+
+import com.android.contacts.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.DialogInterface.OnClickListener;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Intents.Insert;
+import android.text.TextUtils;
+
+/**
+ * Activity that intercepts DIAL and VIEW intents for phone numbers for devices that can not
+ * be used as a phone. This allows the user to see the phone number
+ */
+public class NonPhoneActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final String phoneNumber = getPhoneNumber();
+ if (TextUtils.isEmpty(phoneNumber)) {
+ finish();
+ return;
+ }
+
+ final NonPhoneDialogFragment fragment = new NonPhoneDialogFragment();
+ fragment.setArguments(Bundle.forPair("PHONE_NUMBER", phoneNumber));
+ getFragmentManager().openTransaction().add(fragment, "Fragment").commit();
+ }
+
+ private String getPhoneNumber() {
+ if (getIntent() == null) return null;
+ final Uri data = getIntent().getData();
+ if (data == null) return null;
+ final String scheme = data.getScheme();
+ if (!"tel".equals(scheme)) return null;
+ return getIntent().getData().getSchemeSpecificPart();
+ }
+
+ public static final class NonPhoneDialogFragment extends DialogFragment
+ implements OnClickListener {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog alertDialog;
+ alertDialog = new AlertDialog.Builder(getActivity()).create();
+ alertDialog.setTitle(R.string.non_phone_caption);
+ alertDialog.setMessage(getArgumentPhoneNumber());
+ alertDialog.setButton(DialogInterface.BUTTON_POSITIVE,
+ getActivity().getString(R.string.non_phone_add_to_contacts), this);
+ alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
+ getActivity().getString(R.string.non_phone_close), this);
+ alertDialog.setOnDismissListener(this);
+ return alertDialog;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ intent.setType(Contacts.CONTENT_ITEM_TYPE);
+ intent.putExtra(Insert.PHONE, getArgumentPhoneNumber());
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_NO_HISTORY);
+ startActivity(intent);
+ }
+ dismiss();
+ }
+
+ private String getArgumentPhoneNumber() {
+ return getArguments().getPairValue();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ // During screen rotation, getActivity returns null. In this case we do not
+ // want to close the Activity anyway
+ final Activity activity = getActivity();
+ if (activity != null) activity.finish();
+ }
+ }
+}
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index e38085a..a4f4b72 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -684,8 +684,7 @@
mAizy.setListener(new ContactListAizyView.Listener() {
@Override
public void onScroll(int position) {
- mListView.smoothScrollToPositionFromTop(position + mListView.getHeaderViewsCount(),
- 0);
+ mListView.setSelectionFromTop(position + mListView.getHeaderViewsCount(), 0);
}
});
diff --git a/src/com/android/contacts/quickcontact/QuickContactWindow.java b/src/com/android/contacts/quickcontact/QuickContactWindow.java
index 454ac70..cf2e81d 100644
--- a/src/com/android/contacts/quickcontact/QuickContactWindow.java
+++ b/src/com/android/contacts/quickcontact/QuickContactWindow.java
@@ -1180,6 +1180,11 @@
*/
private void handleData(Cursor cursor) {
if (cursor == null) return;
+ if (cursor.getCount() == 0) {
+ Toast.makeText(mContext, R.string.invalidContactMessage, Toast.LENGTH_LONG).show();
+ dismiss();
+ return;
+ }
if (!isMimeExcluded(Contacts.CONTENT_ITEM_TYPE)) {
// Add the profile shortcut action
diff --git a/src/com/android/contacts/util/PhoneCapabilityTester.java b/src/com/android/contacts/util/PhoneCapabilityTester.java
index fa34b92..961e3c9 100644
--- a/src/com/android/contacts/util/PhoneCapabilityTester.java
+++ b/src/com/android/contacts/util/PhoneCapabilityTester.java
@@ -22,10 +22,19 @@
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.net.sip.SipManager;
+import android.telephony.TelephonyManager;
import java.util.List;
+/**
+ * Provides static functions to quickly test the capabilities of this device. The static
+ * members are not safe for threading
+ */
public final class PhoneCapabilityTester {
+ private static boolean sIsInitialized;
+ private static boolean sIsPhone;
+ private static boolean sIsSipPhone;
+
/**
* Tests whether the Intent has a receiver registered. This can be used to show/hide
* functionality (like Phone, SMS)
@@ -41,28 +50,31 @@
* Returns true if this device can be used to make phone calls
*/
public static boolean isPhone(Context context) {
+ if (!sIsInitialized) initialize(context);
// Is the device physically capabable of making phone calls?
- if (!context.getResources().getBoolean(com.android.internal.R.bool.config_voice_capable)) {
- return false;
- }
+ return sIsPhone;
+ }
- // Is there an app registered that accepts the call intent?
- final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- Uri.fromParts(Constants.SCHEME_TEL, "", null));
- return isIntentRegistered(context, intent);
+ private static void initialize(Context context) {
+ final TelephonyManager telephonyManager = new TelephonyManager(context);
+ sIsPhone = telephonyManager.isVoiceCapable();
+ sIsSipPhone = SipManager.isVoipSupported(context);
+ sIsInitialized = true;
}
/**
* Returns true if this device can be used to make sip calls
*/
public static boolean isSipPhone(Context context) {
- return SipManager.isVoipSupported(context);
+ if (!sIsInitialized) initialize(context);
+ return sIsSipPhone;
}
/**
* Returns true if the device has an SMS application installed.
*/
public static boolean isSmsIntentRegistered(Context context) {
+ // Don't cache the result as the user might install third party apps to send SMS
final Intent intent = new Intent(Intent.ACTION_SENDTO,
Uri.fromParts(Constants.SCHEME_SMSTO, "", null));
return isIntentRegistered(context, intent);
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 54f7939..c41ff55 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -23,8 +23,6 @@
<application>
<uses-library android:name="android.test.runner" />
<meta-data android:name="com.android.contacts.iconset" android:resource="@xml/iconset" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
- <uses-permission android:name="android.permission.GET_ACCOUNTS" />
<activity android:name=".allintents.AllIntentsActivity"
android:label="@string/contactsIntents"
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
index 419ae98..e7649ca 100644
--- a/tests/res/values/donottranslate_strings.xml
+++ b/tests/res/values/donottranslate_strings.xml
@@ -82,6 +82,15 @@
<item>VIEW (lookup uri)</item>
<item>VIEW (called for raw contact)</item>
<item>VIEW (legacy style uri)</item>
+
+ <!-- Various ways to start Contacts -->
+ <item>DIAL</item>
+ <item>DIAL phone (deprecated)</item>
+ <item>DIAL person (deprecated)</item>
+ <item>DIAL voicemail</item>
+ <item>CALL BUTTON</item>
+ <item>DIAL tel</item>
+ <item>VIEW tel</item>
</string-array>
<string name="pinnedHeaderList">Pinned Headers</string>
diff --git a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
index 7543add..b3048f5 100644
--- a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
+++ b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
@@ -126,6 +126,14 @@
private static final int VIEW_RAW_CONTACT = 56;
private static final int VIEW_LEGACY = 57;
+ private static final int DIAL = 58;
+ private static final int DIAL_phone = 59;
+ private static final int DIAL_person = 60;
+ private static final int DIAL_voicemail = 61;
+ private static final int CALL_BUTTON = 62;
+ private static final int DIAL_tel = 63;
+ private static final int VIEW_tel = 64;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -506,6 +514,47 @@
startActivity(intent);
break;
}
+ case DIAL: {
+ startActivity(new Intent(Intent.ACTION_DIAL));
+ break;
+ }
+ case DIAL_phone: {
+ // This is the legacy URI (there is no >2.0 way to call a phone data item)
+ final long dataId = findArbitraryPhoneDataId();
+ if (dataId != -1) {
+ final Uri legacyContentUri = Uri.parse("content://contacts/phones");
+ final Uri uri = ContentUris.withAppendedId(legacyContentUri, dataId);
+ startActivity(new Intent(Intent.ACTION_DIAL, uri));
+ }
+ break;
+ }
+ case DIAL_person: {
+ // This is the legacy URI (there is no >2.0 way to call a person)
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ if (contactId != -1) {
+ final Uri legacyContentUri = Uri.parse("content://contacts/people");
+ final long rawContactId = findArbitraryRawContactOfContact(contactId);
+ final Uri uri = ContentUris.withAppendedId(legacyContentUri, rawContactId);
+ startActivity(new Intent(Intent.ACTION_DIAL, uri));
+ }
+ break;
+ }
+ case DIAL_voicemail: {
+ startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse("voicemail:")));
+ break;
+ }
+ case CALL_BUTTON: {
+ startActivity(new Intent(Intent.ACTION_CALL_BUTTON));
+ break;
+ }
+ case DIAL_tel: {
+ startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse("tel:555-123-4567")));
+ break;
+ }
+ case VIEW_tel: {
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("tel:555-123-4567")));
+ break;
+ }
default: {
Toast.makeText(this, "Sorry, we forgot to write this...", Toast.LENGTH_LONG).show();
}
@@ -570,6 +619,22 @@
return -1;
}
+ private long findArbitraryPhoneDataId() {
+ final Cursor cursor = getContentResolver().query(Data.CONTENT_URI,
+ new String[] { Data._ID },
+ Data.MIMETYPE + "=" + Phone.MIMETYPE,
+ null, "RANDOM() LIMIT 1");
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getLong(0);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ return -1;
+ }
+
private long findArbitraryRawContactOfContact(long contactId) {
final Cursor cursor = getContentResolver().query(RawContacts.CONTENT_URI,
new String[] { RawContacts._ID },