Merge "Make quick contact badge accessible."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a1829d1..f4dcdf6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -23,6 +23,7 @@
<uses-permission android:name="android.permission.CALL_PRIVILEGED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
+ <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
diff --git a/res/drawable-hdpi/btn_dial_pressed.png b/res/drawable-hdpi/btn_dial_pressed.png
deleted file mode 100644
index 9c73cca..0000000
--- a/res/drawable-hdpi/btn_dial_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/btn_dial_selected.9.png b/res/drawable-hdpi/btn_dial_selected.9.png
deleted file mode 100644
index 6372fa0..0000000
--- a/res/drawable-hdpi/btn_dial_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/list_selector_disabled_holo_dark.9.png b/res/drawable-hdpi/list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000..f6fd30d
--- /dev/null
+++ b/res/drawable-hdpi/list_selector_disabled_holo_dark.9.png
Binary files differ
diff --git a/res/drawable-mdpi/btn_dial_pressed.png b/res/drawable-mdpi/btn_dial_pressed.png
deleted file mode 100644
index 2440553..0000000
--- a/res/drawable-mdpi/btn_dial_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/btn_dial_selected.9.png b/res/drawable-mdpi/btn_dial_selected.9.png
deleted file mode 100644
index f9258a1..0000000
--- a/res/drawable-mdpi/btn_dial_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/list_selector_disabled_holo_dark.9.png b/res/drawable-mdpi/list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000..92da2f0
--- /dev/null
+++ b/res/drawable-mdpi/list_selector_disabled_holo_dark.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/btn_dial_pressed.png b/res/drawable-xhdpi/btn_dial_pressed.png
deleted file mode 100644
index 27c95b7..0000000
--- a/res/drawable-xhdpi/btn_dial_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/btn_dial_selected.9.png b/res/drawable-xhdpi/btn_dial_selected.9.png
deleted file mode 100644
index 1e2fe56..0000000
--- a/res/drawable-xhdpi/btn_dial_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/list_selector_disabled_holo_dark.9.png b/res/drawable-xhdpi/list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000..88726b6
--- /dev/null
+++ b/res/drawable-xhdpi/list_selector_disabled_holo_dark.9.png
Binary files differ
diff --git a/res/drawable/btn_call.xml b/res/drawable/btn_call.xml
index 51c2b7d..73ff87b 100644
--- a/res/drawable/btn_call.xml
+++ b/res/drawable/btn_call.xml
@@ -14,12 +14,20 @@
limitations under the License.
-->
-<!-- Background resource for call button in the various dialpads -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"
- android:drawable="@drawable/btn_call_pressed" />
- <item android:state_focused="true"
- android:drawable="@drawable/btn_dial_selected" />
- <item
- android:drawable="@android:color/transparent" />
+<!-- Background resource for call button in the various dialpads.
+ Almost a copy from framework's item_background_holo_dark.xml, but has different pressed effect
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+
+ <item android:state_window_focused="false" android:drawable="@android:color/transparent" />
+
+ <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+ <item android:state_focused="true" android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_dark" />
+ <item android:state_focused="true" android:state_enabled="false" android:drawable="@drawable/list_selector_disabled_holo_dark" />
+ <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/btn_call_pressed" />
+ <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/btn_call_pressed" />
+ <item android:state_focused="true" android:drawable="@drawable/list_focused_holo" />
+ <item android:drawable="@android:color/transparent" />
</selector>
+
diff --git a/res/drawable/btn_dial.xml b/res/drawable/btn_dial.xml
deleted file mode 100644
index 2a577e0..0000000
--- a/res/drawable/btn_dial.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<!-- Background resource for digit buttons in the various dialpads
- used by the Contacts app (see dialpad.xml).
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"
- android:drawable="@drawable/btn_dial_pressed" />
- <item android:state_focused="true"
- android:drawable="@drawable/btn_dial_selected" />
-
- <item
- android:drawable="@android:color/transparent" />
-</selector>
diff --git a/res/layout/call_detail.xml b/res/layout/call_detail.xml
index 936bca8..a890ddf 100644
--- a/res/layout/call_detail.xml
+++ b/res/layout/call_detail.xml
@@ -156,7 +156,7 @@
android:orientation="vertical"
android:gravity="center_vertical"
android:focusable="true"
- android:background="@drawable/btn_dial"
+ android:background="?android:attr/selectableItemBackground"
>
<TextView android:id="@+id/call_and_sms_text"
@@ -191,7 +191,7 @@
android:gravity="center"
android:scaleType="centerInside"
android:focusable="true"
- android:background="@drawable/btn_dial"
+ android:background="?android:attr/selectableItemBackground"
/>
</LinearLayout>
</FrameLayout>
diff --git a/res/layout/call_log_fragment.xml b/res/layout/call_log_fragment.xml
index 26689be..852b6f7 100644
--- a/res/layout/call_log_fragment.xml
+++ b/res/layout/call_log_fragment.xml
@@ -46,6 +46,8 @@
android:layout_height="match_parent"
android:text="@string/recentCalls_empty"
android:gravity="center"
+ android:layout_marginTop="@dimen/empty_message_top_margin"
+ android:textColor="?android:attr/textColorSecondary"
android:textAppearance="?android:attr/textAppearanceLarge"
/>
</FrameLayout>
diff --git a/res/layout/contact_detail_fragment_carousel.xml b/res/layout/contact_detail_fragment_carousel.xml
index 4f65549..2572cc4 100644
--- a/res/layout/contact_detail_fragment_carousel.xml
+++ b/res/layout/contact_detail_fragment_carousel.xml
@@ -30,7 +30,9 @@
android:id="@+id/about_fragment_container"
android:layout_width="0dip"
android:layout_height="match_parent"
- android:layout_weight="1" />
+ android:layout_weight="1"
+ android:focusable="true"
+ android:focusableInTouchMode="true" />
<!--
Container for the "Updates" page fragment on the contact card for a contact
@@ -42,6 +44,8 @@
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
- android:visibility="gone" />
+ android:visibility="gone"
+ android:focusable="true"
+ android:focusableInTouchMode="true" />
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/dialpad_additional_buttons.xml b/res/layout/dialpad_additional_buttons.xml
index f0e4a42..ae37fd5 100644
--- a/res/layout/dialpad_additional_buttons.xml
+++ b/res/layout/dialpad_additional_buttons.xml
@@ -33,7 +33,7 @@
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:state_enabled="false"
- android:background="@drawable/btn_dial"
+ android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/description_search_button"
android:src="@drawable/ic_see_contacts_holo_dark"/>
@@ -57,7 +57,7 @@
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:state_enabled="false"
- android:background="@drawable/btn_dial"
+ android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/description_delete_button"
android:src="@drawable/ic_dial_action_delete" />
</LinearLayout>
diff --git a/res/layout/dialpad_fragment.xml b/res/layout/dialpad_fragment.xml
index b2784e2..96cf543 100644
--- a/res/layout/dialpad_fragment.xml
+++ b/res/layout/dialpad_fragment.xml
@@ -30,30 +30,29 @@
android:layout_height="0px"
android:layout_weight="0.200"
android:layout_marginTop="@dimen/dialpad_vertical_margin"
- android:gravity="center" >
+ android:gravity="center"
+ android:background="@drawable/dialpad_background" >
- <!-- Type of this EditText is set to NULL (to disable the IME keyboard)
- in the java code.
-
- Background drawable can be controlled programatically. -->
<com.android.contacts.dialpad.DigitsEditText
android:id="@+id/digits"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
- android:layout_toLeftOf="@+id/moreoverflow"
- android:background="@drawable/dialpad_background"
android:gravity="center"
android:textAppearance="@style/DialtactsDigitsTextAppearance"
- android:textColor="?android:attr/textColorPrimary" />
+ android:textColor="?android:attr/textColorPrimary"
+ android:nextFocusRight="@+id/overflow_menu"
+ android:background="@android:color/transparent" />
- <ImageButton android:id="@+id/overflow_menu"
+ <ImageButton
+ android:id="@+id/overflow_menu"
android:layout_width="48dip"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:src="@drawable/ic_menu_overflow"
android:contentDescription="@*android:string/action_menu_overflow_description"
- android:background="@android:color/transparent"/>
+ android:nextFocusLeft="@id/digits"
+ android:background="?android:attr/selectableItemBackground"/>
</RelativeLayout>
diff --git a/res/layout/dialtacts_custom_action_bar.xml b/res/layout/dialtacts_custom_action_bar.xml
index 2be66f7..0709626 100644
--- a/res/layout/dialtacts_custom_action_bar.xml
+++ b/res/layout/dialtacts_custom_action_bar.xml
@@ -31,11 +31,12 @@
<ImageButton
android:id="@+id/search_option"
android:layout_width="wrap_content"
- android:paddingRight="8dip"
+ android:paddingLeft="4dip"
+ android:paddingRight="4dip"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:src="@drawable/ic_menu_overflow"
- android:background="@android:color/transparent"
+ android:background="?android:attr/selectableItemBackground"
android:visibility="gone" />
</LinearLayout>
diff --git a/res/layout/favorites_star.xml b/res/layout/favorites_star.xml
index 5bdc091..2ac3039 100644
--- a/res/layout/favorites_star.xml
+++ b/res/layout/favorites_star.xml
@@ -30,4 +30,4 @@
android:contentDescription="@string/description_star"
android:visibility="invisible"
android:button="@drawable/btn_star_holo_dark"/>
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/res/layout/playback_layout.xml b/res/layout/playback_layout.xml
index c2fdb3e..711ebf8 100644
--- a/res/layout/playback_layout.xml
+++ b/res/layout/playback_layout.xml
@@ -24,7 +24,7 @@
android:id="@+id/playback_start_stop"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/btn_dial"
+ android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_hold_pause"
/>
</LinearLayout>
@@ -38,7 +38,7 @@
android:id="@+id/playback_speakerphone"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/btn_dial"
+ android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_speakerphone_on"
/>
</LinearLayout>
@@ -97,7 +97,7 @@
android:src="@drawable/ic_minus"
android:layout_width="64dip"
android:layout_height="wrap_content"
- android:background="@drawable/btn_dial"
+ android:background="?android:attr/selectableItemBackground"
android:paddingBottom="19dip"
android:paddingTop="29dip"
android:layout_alignParentLeft="true"
@@ -108,7 +108,7 @@
android:src="@drawable/ic_plus"
android:layout_width="64dip"
android:layout_height="wrap_content"
- android:background="@drawable/btn_dial"
+ android:background="?android:attr/selectableItemBackground"
android:paddingBottom="19dip"
android:paddingTop="29dip"
android:layout_alignParentRight="true"
diff --git a/res/mipmap-hdpi/ic_launcher_contacts.png b/res/mipmap-hdpi/ic_launcher_contacts.png
index 5ebe2e0..fa60a53 100644
--- a/res/mipmap-hdpi/ic_launcher_contacts.png
+++ b/res/mipmap-hdpi/ic_launcher_contacts.png
Binary files differ
diff --git a/res/mipmap-hdpi/ic_launcher_phone.png b/res/mipmap-hdpi/ic_launcher_phone.png
index e56c507..5a3dff1 100644
--- a/res/mipmap-hdpi/ic_launcher_phone.png
+++ b/res/mipmap-hdpi/ic_launcher_phone.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_contacts.png b/res/mipmap-mdpi/ic_launcher_contacts.png
index c79ae14..b50d7dd 100644
--- a/res/mipmap-mdpi/ic_launcher_contacts.png
+++ b/res/mipmap-mdpi/ic_launcher_contacts.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_phone.png b/res/mipmap-mdpi/ic_launcher_phone.png
index 1f40fd0..9ea0d8c 100644
--- a/res/mipmap-mdpi/ic_launcher_phone.png
+++ b/res/mipmap-mdpi/ic_launcher_phone.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_contacts.png b/res/mipmap-xhdpi/ic_launcher_contacts.png
index ca3aa63..4b7eaaa 100644
--- a/res/mipmap-xhdpi/ic_launcher_contacts.png
+++ b/res/mipmap-xhdpi/ic_launcher_contacts.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_phone.png b/res/mipmap-xhdpi/ic_launcher_phone.png
index e23a58c..e97836c 100644
--- a/res/mipmap-xhdpi/ic_launcher_phone.png
+++ b/res/mipmap-xhdpi/ic_launcher_phone.png
Binary files differ
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 4317050..0b47d5a 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -237,7 +237,7 @@
<item name="android:layout_width">0dip</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_weight">1</item>
- <item name="android:background">@drawable/btn_dial</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
<item name="android:soundEffectsEnabled">false</item>
</style>
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 2df2e47..e465772 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -323,10 +323,26 @@
if (rawContactId == -1) {
throw new IllegalStateException("Could not determine RawContact ID after save");
}
- final Uri rawContactUri = ContentUris.withAppendedId(
- isProfile ? Profile.CONTENT_RAW_CONTACTS_URI : RawContacts.CONTENT_URI,
- rawContactId);
- lookupUri = RawContacts.getContactLookupUri(resolver, rawContactUri);
+ if (isProfile) {
+ // Since the profile supports local raw contacts, which may have been completely
+ // removed if all information was removed, we need to do a special query to
+ // get the lookup URI for the profile contact (if it still exists).
+ Cursor c = resolver.query(Profile.CONTENT_URI,
+ new String[] {Contacts._ID, Contacts.LOOKUP_KEY},
+ null, null, null);
+ try {
+ c.moveToFirst();
+ final long contactId = c.getLong(0);
+ final String lookupKey = c.getString(1);
+ lookupUri = Contacts.getLookupUri(contactId, lookupKey);
+ } finally {
+ c.close();
+ }
+ } else {
+ final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
+ rawContactId);
+ lookupUri = RawContacts.getContactLookupUri(resolver, rawContactUri);
+ }
Log.v(TAG, "Saved contact. New URI: " + lookupUri);
break;
diff --git a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
index 3d4b70f..95a39e7 100644
--- a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
+++ b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
@@ -96,6 +96,7 @@
private TextView mReadOnlyWarningView;
private ImageView mPhotoView;
private ViewGroup mEditorContainerView;
+ private static WeakReference<ProgressDialog> sProgressDialog;
private AccountTypeManager mAccountTypeManager;
private ContentResolver mContentResolver;
@@ -691,7 +692,6 @@
private static final int RESULT_FAILURE = 2;
private ConfirmAddDetailActivity activityTarget;
- private WeakReference<ProgressDialog> mProgress;
private AccountTypeManager mAccountTypeManager;
@@ -702,8 +702,8 @@
@Override
protected void onPreExecute() {
- mProgress = new WeakReference<ProgressDialog>(ProgressDialog.show(activityTarget, null,
- activityTarget.getText(R.string.savingContact)));
+ sProgressDialog = new WeakReference<ProgressDialog>(ProgressDialog.show(activityTarget,
+ null, activityTarget.getText(R.string.savingContact)));
// Before starting this task, start an empty service to protect our
// process from being reclaimed by the system.
@@ -760,8 +760,7 @@
protected void onPostExecute(Integer result) {
final Context context = activityTarget;
- // Dismiss the progress dialog
- mProgress.get().dismiss();
+ dismissProgressDialog();
// Show a toast message based on the success or failure of the save action.
if (result == RESULT_SUCCESS) {
@@ -776,6 +775,24 @@
}
}
+ @Override
+ protected void onStop() {
+ super.onStop();
+ // Dismiss the progress dialog here to prevent leaking the window on orientation change.
+ dismissProgressDialog();
+ }
+
+ /**
+ * Dismiss the progress dialog (check if it is null because it is a {@link WeakReference}).
+ */
+ private static void dismissProgressDialog() {
+ ProgressDialog dialog = (sProgressDialog == null) ? null : sProgressDialog.get();
+ if (dialog != null) {
+ dialog.dismiss();
+ }
+ sProgressDialog = null;
+ }
+
/**
* This method is intended to be executed after the background task for saving edited info has
* finished. The method sets the activity result (and intent if applicable) and finishes the
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 332a953..d2759dc 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -53,6 +53,7 @@
import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.preference.ContactsPreferenceActivity;
import com.android.contacts.preference.DisplayOptionsPreferenceFragment;
+import com.android.contacts.util.AccountPromptUtils;
import com.android.contacts.util.AccountSelectionUtil;
import com.android.contacts.util.AccountsListAdapter;
import com.android.contacts.util.Constants;
@@ -918,6 +919,16 @@
mAllFragment.setEnabled(true);
}
} else {
+ // If there are no accounts on the device and we should show the "no account" prompt
+ // (based on {@link SharedPreferences}), then launch the account setup activity so the
+ // user can sign-in or create an account.
+ if (!areAccountsAvailable() && AccountPromptUtils.shouldShowAccountPrompt(this)) {
+ AccountPromptUtils.launchAccountPrompt(this);
+ return;
+ }
+
+ // Otherwise, continue setting up the page so that the user can still use the app
+ // without an account.
if (mAllFragment != null) {
mAllFragment.setEnabled(false);
}
diff --git a/src/com/android/contacts/calllog/CallLogAdapter.java b/src/com/android/contacts/calllog/CallLogAdapter.java
index 7666a7a..4223146 100644
--- a/src/com/android/contacts/calllog/CallLogAdapter.java
+++ b/src/com/android/contacts/calllog/CallLogAdapter.java
@@ -584,7 +584,13 @@
ExpirableCache.CachedValue<ContactInfo> cachedInfo =
mContactInfoCache.getCachedValue(number);
ContactInfo info = cachedInfo == null ? null : cachedInfo.getValue();
- if (cachedInfo == null) {
+ if (!mPhoneNumberHelper.canPlaceCallsTo(number)
+ || mPhoneNumberHelper.isVoicemailNumber(number)) {
+ // If this is a number that cannot be dialed, there is no point in looking up a contact
+ // for it.
+ info = ContactInfo.EMPTY;
+ formattedNumber = null;
+ } else if (cachedInfo == null) {
mContactInfoCache.put(number, ContactInfo.EMPTY);
// Use the cached contact info from the call log.
info = cachedContactInfo;
diff --git a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
index b01316b..756b1c7 100644
--- a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
@@ -19,7 +19,6 @@
import com.android.contacts.R;
import android.content.Context;
-import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -88,8 +87,6 @@
private View mDetailFragmentView;
private View mUpdatesFragmentView;
- private boolean mScrollToCurrentPage = false;
-
public ContactDetailFragmentCarousel(Context context) {
this(context, null);
}
@@ -144,31 +141,9 @@
resolveSize(screenHeight, heightMeasureSpec));
}
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- if (mScrollToCurrentPage) {
- mScrollToCurrentPage = false;
- // Use scrollTo() instead of smoothScrollTo() to prevent a visible flicker to the user
- scrollTo(mCurrentPage == ABOUT_PAGE ? 0 : mAllowedHorizontalScrollLength, 0);
- updateTouchInterceptors();
- }
- }
-
/**
- * Set the current page that should be restored when the view is first laid out.
- */
- public void restoreCurrentPage(int pageIndex) {
- setCurrentPage(pageIndex);
- // It is only possible to scroll the view after onMeasure() has been called (where the
- // allowed horizontal scroll length is determined). Hence, set a flag that will be read
- // in onLayout() after the children and this view have finished being laid out.
- mScrollToCurrentPage = true;
- }
-
- /**
- * Set the current page. This auto-scrolls the carousel to the current page and dims out
- * the non-selected page.
+ * Set the current page. This dims out the non-selected page but doesn't do any scrolling of
+ * the carousel.
*/
public void setCurrentPage(int pageIndex) {
mCurrentPage = pageIndex;
@@ -176,7 +151,6 @@
if (mAboutFragment != null && mUpdatesFragment != null) {
mAboutFragment.setAlphaLayerValue(mCurrentPage == ABOUT_PAGE ? 0 : MAX_ALPHA);
mUpdatesFragment.setAlphaLayerValue(mCurrentPage == UPDATES_PAGE ? 0 : MAX_ALPHA);
- snapToEdge();
}
}
@@ -205,9 +179,12 @@
mEnableSwipe = enable;
if (mUpdatesFragmentView != null) {
mUpdatesFragmentView.setVisibility(enable ? View.VISIBLE : View.GONE);
- mScrollToCurrentPage = true;
- requestLayout();
- invalidate();
+ if (mCurrentPage == ABOUT_PAGE) {
+ mDetailFragmentView.requestFocus();
+ } else {
+ mUpdatesFragmentView.requestFocus();
+ }
+ updateTouchInterceptors();
}
}
}
diff --git a/src/com/android/contacts/detail/ContactDetailLayoutController.java b/src/com/android/contacts/detail/ContactDetailLayoutController.java
index 7a7f400..4b31a6e 100644
--- a/src/com/android/contacts/detail/ContactDetailLayoutController.java
+++ b/src/com/android/contacts/detail/ContactDetailLayoutController.java
@@ -215,7 +215,7 @@
mFragmentCarousel.setFragmentViews(mDetailFragmentView, mUpdatesFragmentView);
mFragmentCarousel.setFragments(mDetailFragment, mUpdatesFragment);
- mFragmentCarousel.restoreCurrentPage(currentPageIndex);
+ mFragmentCarousel.setCurrentPage(currentPageIndex);
break;
}
}
diff --git a/src/com/android/contacts/util/AccountPromptUtils.java b/src/com/android/contacts/util/AccountPromptUtils.java
new file mode 100644
index 0000000..58865d0
--- /dev/null
+++ b/src/com/android/contacts/util/AccountPromptUtils.java
@@ -0,0 +1,115 @@
+/*
+ * 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.util;
+
+import com.android.contacts.R;
+import com.android.contacts.model.GoogleAccountType;
+
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Utility class for controlling whether the standard "no account" prompt on launch is shown.
+ */
+public class AccountPromptUtils {
+
+ private static final String TAG = AccountPromptUtils.class.getSimpleName();
+
+ /** {@link SharedPreferences} key for whether or not the "no account" prompt should be shown. */
+ private static final String KEY_SHOW_ACCOUNT_PROMPT = "settings.showAccountPrompt";
+
+ /**
+ * The following intent keys are understood by the {@link AccountManager} and should not be
+ * changed unless the API changes.
+ */
+ private static final String KEY_INTRO_MESSAGE = "introMessage";
+ private static final String KEY_ALLOW_SKIP_ACCOUNT_SETUP = "allowSkip";
+ private static final String KEY_USER_SKIPPED_ACCOUNT_SETUP = "setupSkipped";
+
+ private static SharedPreferences getSharedPreferences(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context);
+ }
+
+ /**
+ * Returns true if the "no account" prompt should be shown
+ * (according to {@link SharedPreferences}), otherwise return false.
+ */
+ public static boolean shouldShowAccountPrompt(Context context) {
+ return getSharedPreferences(context).getBoolean(KEY_SHOW_ACCOUNT_PROMPT, true);
+ }
+
+ /**
+ * Remember to never show the "no account" prompt again by saving this to
+ * {@link SharedPreferences}.
+ */
+ public static void neverShowAccountPromptAgain(Context context) {
+ getSharedPreferences(context).edit()
+ .putBoolean(KEY_SHOW_ACCOUNT_PROMPT, false)
+ .apply();
+ }
+
+ /**
+ * Launch the "no account" prompt. (We assume the caller has already verified that the prompt
+ * can be shown, so checking the {@link #KEY_SHOW_ACCOUNT_PROMPT} value in
+ * {@link SharedPreferences} will not be done in this method).
+ */
+ public static void launchAccountPrompt(Activity activity) {
+ Bundle options = new Bundle();
+ options.putCharSequence(KEY_INTRO_MESSAGE, activity.getString(R.string.no_account_prompt));
+ options.putBoolean(KEY_ALLOW_SKIP_ACCOUNT_SETUP, true);
+ AccountManager.get(activity).addAccount(GoogleAccountType.ACCOUNT_TYPE, null, null, options,
+ activity, getAccountManagerCallback(activity), null);
+ }
+
+ private static AccountManagerCallback<Bundle> getAccountManagerCallback(
+ final Activity activity) {
+ return new AccountManagerCallback<Bundle>() {
+ @Override
+ public void run(AccountManagerFuture<Bundle> future) {
+ if (future.isCancelled()) {
+ // The account creation process was canceled
+ activity.finish();
+ return;
+ }
+ try {
+ Bundle result = future.getResult();
+ if (result.getBoolean(KEY_USER_SKIPPED_ACCOUNT_SETUP)) {
+ AccountPromptUtils.neverShowAccountPromptAgain(activity);
+ }
+ } catch (OperationCanceledException ignore) {
+ Log.e(TAG, "Account setup error: account creation process canceled");
+ } catch (IOException ignore) {
+ Log.e(TAG, "Account setup error: No authenticator was registered for this"
+ + "account type or the authenticator failed to respond");
+ } catch (AuthenticatorException ignore) {
+ Log.e(TAG, "Account setup error: Authenticator experienced an I/O problem");
+ }
+ }
+ };
+ }
+}
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index 054e018..18b9cc1 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -45,18 +45,14 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
+import android.database.Cursor;
import android.net.Uri;
-import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
-import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
import android.os.PowerManager;
-import android.os.RemoteException;
+import android.provider.OpenableColumns;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
@@ -72,7 +68,6 @@
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
-import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -285,6 +280,8 @@
return;
}
} else {
+ final ContentResolver resolver =
+ ImportVCardActivity.this.getContentResolver();
for (Uri sourceUri : mSourceUris) {
String filename = null;
// Note: caches are removed by VCardService.
@@ -309,10 +306,37 @@
Log.w(LOG_TAG, "destUri is null");
break;
}
+
+ String displayName = null;
+ Cursor cursor = null;
+ // Try to get a display name from the given Uri. If it fails, we just
+ // pick up the last part of the Uri.
+ try {
+ cursor = resolver.query(sourceUri,
+ new String[] { OpenableColumns.DISPLAY_NAME },
+ null, null, null);
+ if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) {
+ if (cursor.getCount() > 1) {
+ Log.w(LOG_TAG, "Unexpected multiple rows: "
+ + cursor.getCount());
+ }
+ int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
+ if (index >= 0) {
+ displayName = cursor.getString(index);
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ if (TextUtils.isEmpty(displayName)){
+ displayName = sourceUri.getLastPathSegment();
+ }
+
final ImportRequest request;
try {
- request = constructImportRequest(null, localDataUri,
- sourceUri.getLastPathSegment());
+ request = constructImportRequest(null, localDataUri, displayName);
} catch (VCardException e) {
Log.e(LOG_TAG, "Maybe the file is in wrong format", e);
showFailureNotification(R.string.fail_reason_not_supported);