Merge "Don't show welcome screen when talkback is on and on pre-L-MR1" into ub-contactsdialer-h-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6441042..6144e55 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,8 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.contacts"
- android:versionCode="20000"
- android:versionName="2.0.0">
+ android:versionCode="10605"
+ android:versionName="1.6.5">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="25" />
@@ -357,13 +357,6 @@
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
- <action android:name="android.intent.action.EDIT" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="vnd.android.cursor.item/person" />
- <data android:mimeType="vnd.android.cursor.item/contact" />
- <data android:mimeType="vnd.android.cursor.item/raw_contact" />
- </intent-filter>
- <intent-filter>
<action android:name="android.intent.action.INSERT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/person" />
@@ -372,6 +365,22 @@
</intent-filter>
</activity>
+ <activity
+ android:name=".activities.ContactEditorSpringBoardActivity"
+ android:noHistory="true"
+ android:theme="@style/TransparentThemeAppCompat">
+
+ <intent-filter>
+ <action android:name="android.intent.action.EDIT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.cursor.item/person" />
+ <data android:mimeType="vnd.android.cursor.item/contact" />
+ <data android:mimeType="vnd.android.cursor.item/raw_contact" />
+ </intent-filter>
+
+ </activity>
+
+
<activity android:name=".common.test.FragmentTestActivity">
<intent-filter>
<category android:name="android.intent.category.TEST" />
diff --git a/proguard.flags b/proguard.flags
index b7b9f18..80ffe0a 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -73,6 +73,7 @@
-keep class com.android.contacts.common.util.DeviceLocalAccountTypeFactory { *; }
-keep class com.android.contacts.common.util.DeviceLocalAccountTypeFactory$* { *; }
-keep class com.android.contacts.common.util.NameConverter { *; }
+-keep class com.android.contacts.common.util.PermissionsUtil { *; }
-keep class com.android.contacts.common.util.SearchUtil { *; }
-keep class com.android.contacts.common.util.SearchUtil$* { *; }
-keep class com.android.contacts.ContactsApplication { *; }
diff --git a/res/layout/raw_contact_list_item.xml b/res/layout/raw_contact_list_item.xml
new file mode 100644
index 0000000..ec5de0c
--- /dev/null
+++ b/res/layout/raw_contact_list_item.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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="wrap_content"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_marginEnd="16dp"
+ android:scaleType="fitCenter"/>
+
+ <LinearLayout
+ android:id="@+id/text_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/display_name"
+ android:textSize="16sp"
+ android:textColor="@color/quantum_black_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"/>
+
+ <LinearLayout
+ android:id="@+id/account_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical">
+
+ <ImageView
+ android:id="@+id/account_icon"
+ android:layout_width="14dp"
+ android:layout_height="14dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="4dp"
+ android:scaleType="fitCenter"/>
+
+ <TextView
+ android:id="@+id/account_name"
+ android:textSize="13sp"
+ android:textColor="@color/quantum_black_secondary_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 081d579..98c522d 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -14,6 +14,9 @@
limitations under the License.
-->
<resources>
+ <!-- 87% black -->
+ <color name="quantum_black_text">#dd000000</color>
+
<!-- 54% black -->
<color name="quantum_black_secondary_text">#89000000</color>
@@ -42,6 +45,9 @@
<color name="primary_color_dark">#0277bd</color>
<color name="primary_color">#0288d1</color>
+ <color name="group_primary_color_dark">#546E7A</color>
+ <color name="group_primary_color">#607D8B</color>
+
<!-- Color of the selected tab underline -->
<color name="contacts_accent_color">#FFFFFF</color>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 566bd75..454777e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -693,6 +693,12 @@
at a pre-determined text size. [CHAR LIMIT=20] -->
<string name="contact_editor_title_existing_contact">Edit contact</string>
+ <!-- Dialog title when the user is selecting a raw contact to edit. [CHAR LIMIT=128] -->
+ <string name="contact_editor_pick_raw_contact_dialog_title">Choose linked contact</string>
+
+ <!-- Text next to a contact's name that lets the user know it is a read only contact. [CHAR LIMIT=128] -->
+ <string name="contact_editor_pick_raw_contact_read_only"><xliff:g id="display_name">%s</xliff:g> (read only)</string>
+
<!-- Button label to prompt the user to add an account (when there are 0 existing accounts on the device) [CHAR LIMIT=30] -->
<string name="add_account">Add account</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index e7b6582..d51392a 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -498,4 +498,14 @@
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowElevation">0dp</item>
</style>
+
+ <!-- Transparent/blank activity -->
+ <style name="TransparentThemeAppCompat" parent="@style/PeopleActivityTheme">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ </style>
</resources>
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
index b810eec..3fc3c9d 100644
--- a/src/com/android/contacts/ContactsDrawerActivity.java
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -54,6 +54,7 @@
import com.android.contacts.common.util.AccountFilterUtil;
import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
import com.android.contacts.common.util.ImplicitIntentsUtil;
+import com.android.contacts.common.util.MaterialColorMapUtils;
import com.android.contacts.common.util.ViewUtil;
import com.android.contacts.editor.ContactEditorFragment;
import com.android.contacts.editor.SelectAccountDialogFragment;
@@ -134,6 +135,7 @@
// another fragment in navigation drawer, the current search/selection mode will be
// overlaid by the action bar of the newly-created fragment.
stopSearchAndSelection();
+ updateStatusBarBackground();
}
private void stopSearchAndSelection() {
@@ -170,7 +172,7 @@
super.onDrawerStateChanged(newState);
// Set transparent status bar when drawer starts to move.
if (newState != DrawerLayout.STATE_IDLE) {
- makeStatusBarTransparent();
+ updateStatusBarBackground();
}
if (mRunnable != null && newState == DrawerLayout.STATE_IDLE) {
mRunnable.run();
@@ -285,17 +287,23 @@
protected void onResume() {
super.onResume();
if (mDrawer.isDrawerOpen(GravityCompat.START)) {
- makeStatusBarTransparent();
+ updateStatusBarBackground();
}
}
- private void makeStatusBarTransparent() {
- // Avoid making status bar transparent when action bar's selection mode is on.
- if (getWindow().getStatusBarColor() !=
- ContextCompat.getColor(this, R.color.contextual_selection_bar_status_bar_color)
- && CompatUtils.isLollipopCompatible()) {
- getWindow().setStatusBarColor(Color.TRANSPARENT);
+ public void updateStatusBarBackground() {
+ updateStatusBarBackground(/* color */ -1);
+ }
+
+ public void updateStatusBarBackground(int color) {
+ if (!CompatUtils.isLollipopCompatible()) return;
+ if (color == -1) {
+ mDrawer.setStatusBarBackgroundColor(MaterialColorMapUtils.getStatusBarColor(this));
+ } else {
+ mDrawer.setStatusBarBackgroundColor(color);
}
+ mDrawer.invalidate();
+ getWindow().setStatusBarColor(Color.TRANSPARENT);
}
// Set up fragment manager to load groups and filters.
@@ -417,7 +425,7 @@
return null;
}
- protected boolean isGroupView() {
+ public boolean isGroupView() {
return mCurrentView == ContactsView.GROUP_VIEW;
}
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 824d4ea..880aa63 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -43,9 +43,11 @@
import android.widget.EditText;
import android.widget.TextView;
+import com.android.contacts.ContactsDrawerActivity;
import com.android.contacts.R;
import com.android.contacts.activities.ActionBarAdapter.Listener.Action;
import com.android.contacts.common.compat.CompatUtils;
+import com.android.contacts.common.util.MaterialColorMapUtils;
import com.android.contacts.list.ContactsRequest;
import java.util.ArrayList;
@@ -365,6 +367,8 @@
= (mSearchContainer.getParent() == null) == mSearchMode;
final boolean isTabHeightChanging = isSearchModeChanging || isSelectionModeChanging;
+ // Update toolbar and status bar color.
+ mToolBarFrame.setBackgroundColor(MaterialColorMapUtils.getToolBarColor(mActivity));
updateStatusBarColor(isSelectionModeChanging && !isSearchModeChanging);
// When skipAnimation=true, it is possible that we will switch from search mode
@@ -494,12 +498,11 @@
mActivity, R.color.contextual_selection_bar_status_bar_color);
runStatusBarAnimation(/* colorTo */ cabStatusBarColor);
} else {
- final int normalStatusBarColor = ContextCompat.getColor(
- mActivity, R.color.primary_color_dark);
if (shouldAnimate) {
- runStatusBarAnimation(/* colorTo */ normalStatusBarColor);
- } else {
- mActivity.getWindow().setStatusBarColor(normalStatusBarColor);
+ runStatusBarAnimation(/* colorTo */
+ MaterialColorMapUtils.getStatusBarColor(mActivity));
+ } else if (mActivity instanceof ContactsDrawerActivity) {
+ ((ContactsDrawerActivity) mActivity).updateStatusBarBackground();
}
}
}
diff --git a/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java b/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java
new file mode 100644
index 0000000..f32ee5b
--- /dev/null
+++ b/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java
@@ -0,0 +1,182 @@
+package com.android.contacts.activities;
+
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.app.LoaderManager;
+import android.content.ContentUris;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.RawContacts;
+import android.widget.Toast;
+
+import com.android.contacts.AppCompatContactsActivity;
+import com.android.contacts.R;
+import com.android.contacts.common.activity.RequestPermissionsActivity;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+import com.android.contacts.editor.ContactEditorFragment;
+import com.android.contacts.editor.EditorIntents;
+import com.android.contacts.editor.PickRawContactDialogFragment;
+import com.android.contacts.editor.PickRawContactLoader;
+
+/**
+ * Transparent springboard activity that hosts a dialog to select a raw contact to edit.
+ * This activity has noHistory set to true, and all intents coming out from it have
+ * {@code FLAG_ACTIVITY_FORWARD_RESULT} set.
+ */
+public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity {
+ private static final String TAG = "EditorSpringBoard";
+ private static final String TAG_RAW_CONTACTS_DIALOG = "rawContactsDialog";
+ private static final int LOADER_RAW_CONTACTS = 1;
+
+ private Uri mUri;
+ private Cursor mCursor;
+ private MaterialPalette mMaterialPalette;
+
+ /**
+ * The contact data loader listener.
+ */
+ protected final LoaderManager.LoaderCallbacks<Cursor> mRawContactLoaderListener =
+ new LoaderManager.LoaderCallbacks<Cursor>() {
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ return new PickRawContactLoader(ContactEditorSpringBoardActivity.this, mUri);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
+ if (cursor == null) {
+ Toast.makeText(ContactEditorSpringBoardActivity.this,
+ R.string.editor_failed_to_load, Toast.LENGTH_SHORT).show();
+ finish();
+ return;
+ }
+ mCursor = cursor;
+ if (mCursor.getCount() == 1) {
+ loadEditor();
+ } else {
+ showDialog();
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mCursor = null;
+ }
+ };
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (RequestPermissionsActivity.startPermissionActivity(this)) {
+ return;
+ }
+
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+
+ if (!Intent.ACTION_EDIT.equals(action)) {
+ finish();
+ return;
+ }
+ // Just for shorter variable names.
+ final String primary = ContactEditorFragment.INTENT_EXTRA_MATERIAL_PALETTE_PRIMARY_COLOR;
+ final String secondary =
+ ContactEditorFragment.INTENT_EXTRA_MATERIAL_PALETTE_SECONDARY_COLOR;
+ if (intent.hasExtra(primary) && intent.hasExtra(secondary)) {
+ mMaterialPalette = new MaterialPalette(intent.getIntExtra(primary, -1),
+ intent.getIntExtra(secondary, -1));
+ }
+
+ mUri = intent.getData();
+ final String authority = mUri.getAuthority();
+ final String type = getContentResolver().getType(mUri);
+ // Go straight to editor if we're passed a raw contact Uri.
+ if (ContactsContract.AUTHORITY.equals(authority) &&
+ RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
+ final long rawContactId = ContentUris.parseId(mUri);
+ final Intent editorIntent = getIntentForRawContact(rawContactId);
+ ImplicitIntentsUtil.startActivityInApp(this, editorIntent);
+ } else {
+ getLoaderManager().initLoader(LOADER_RAW_CONTACTS, null, mRawContactLoaderListener);
+ }
+ }
+
+ /**
+ * Start the dialog to pick the raw contact to edit.
+ */
+ private void showDialog() {
+ final FragmentManager fm = getFragmentManager();
+ final PickRawContactDialogFragment oldFragment = (PickRawContactDialogFragment)
+ fm.findFragmentByTag(TAG_RAW_CONTACTS_DIALOG);
+ final FragmentTransaction ft = fm.beginTransaction();
+ if (oldFragment != null) {
+ ft.remove(oldFragment);
+ }
+ final PickRawContactDialogFragment newFragment =
+ PickRawContactDialogFragment.getInstance(mUri, mCursor, mMaterialPalette);
+ ft.add(newFragment, TAG_RAW_CONTACTS_DIALOG);
+ // commitAllowingStateLoss is safe in this activity because the fragment entirely depends
+ // on the result of the loader. Even if we lose the fragment because the activity was
+ // in the background, when it comes back onLoadFinished will be called again which will
+ // have all the state the picker needs. This situation should be very rare, since the load
+ // should be quick.
+ ft.commitAllowingStateLoss();
+ }
+
+ /**
+ * Starts the editor for the first (only) raw contact in the cursor.
+ */
+ private void loadEditor() {
+ final Intent intent;
+ if (isSingleWritableAccount()) {
+ mCursor.moveToFirst();
+ final long rawContactId = mCursor.getLong(PickRawContactLoader.RAW_CONTACT_ID);
+ intent = getIntentForRawContact(rawContactId);
+
+ } else {
+ // If it's a single read-only raw contact, we'll want to let the editor create
+ // the writable raw contact for it.
+ intent = EditorIntents.createEditContactIntent(this, mUri, mMaterialPalette, -1);
+ intent.setClass(this, ContactEditorActivity.class);
+ }
+ ImplicitIntentsUtil.startActivityInApp(this, intent);
+ }
+
+ /**
+ * @return true if there is only one raw contact in the contact and it is from a writable
+ * account.
+ */
+ private boolean isSingleWritableAccount() {
+ if (mCursor.getCount() != 1) {
+ return false;
+ }
+ mCursor.moveToFirst();
+ final String accountType = mCursor.getString(PickRawContactLoader.ACCOUNT_TYPE);
+ final String dataSet = mCursor.getString(PickRawContactLoader.DATA_SET);
+ final AccountType account = AccountTypeManager.getInstance(this)
+ .getAccountType(accountType, dataSet);
+ return account.areContactsWritable();
+ }
+
+ /**
+ * Returns an intent to load the editor for the given raw contact. Sets
+ * {@code FLAG_ACTIVITY_FORWARD_RESULT} in case the activity that started us expects a result.
+ * @param rawContactId Raw contact to edit
+ */
+ private Intent getIntentForRawContact(long rawContactId) {
+ final Intent intent = EditorIntents.createEditContactIntentForRawContact(
+ this, mUri, rawContactId, mMaterialPalette);
+ intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ return intent;
+ }
+}
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index e4dc56d..b2a0a07 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -27,6 +27,8 @@
import android.content.Intent;
import android.content.SyncStatusObserver;
import android.content.IntentFilter;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -34,6 +36,7 @@
import android.provider.ContactsContract.ProviderStatus;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
+import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.SwipeRefreshLayout;
@@ -384,10 +387,7 @@
setUpAllFragment(fragmentManager);
- if (isGroupView()) {
- mMembersFragment = (GroupMembersFragment)
- fragmentManager.findFragmentByTag(TAG_GROUP_VIEW);
- }
+ mMembersFragment = (GroupMembersFragment) fragmentManager.findFragmentByTag(TAG_GROUP_VIEW);
// Configure floating action button
mFloatingActionButtonContainer = findViewById(R.id.floating_action_button_container);
@@ -692,11 +692,13 @@
}
private boolean isAllFragmentInSelectionMode() {
- return mAllFragment.getActionBarAdapter().isSelectionMode();
+ return mAllFragment.getActionBarAdapter() != null
+ && mAllFragment.getActionBarAdapter().isSelectionMode();
}
private boolean isAllFragmentInSearchMode() {
- return mAllFragment.getActionBarAdapter().isSearchMode();
+ return mAllFragment.getActionBarAdapter() != null
+ && mAllFragment.getActionBarAdapter().isSearchMode();
}
@Override
@@ -758,10 +760,11 @@
}
private void switchToOrUpdateGroupView(String action) {
- final boolean shouldUpdate = mMembersFragment != null;
- switchView(ContactsView.GROUP_VIEW);
- if (shouldUpdate) {
+ // If group fragment is active and visible, we simply update it.
+ if (mMembersFragment != null && !mMembersFragment.isInactive()) {
mMembersFragment.updateExistingGroupFragment(mGroupUri, action);
+ } else {
+ switchView(ContactsView.GROUP_VIEW);
}
}
@@ -782,14 +785,21 @@
transaction.replace(
R.id.contacts_list_container, mMembersFragment, TAG_GROUP_VIEW);
} else if (isDuplicatesView()) {
- final Fragment duplicatesFragment = ObjectFactory.getDuplicatesFragment();
- final Fragment duplicatesUtilFragment = ObjectFactory.getDuplicatesUtilFragment();
- if (duplicatesFragment != null && duplicatesUtilFragment != null) {
+ Fragment duplicatesFragment = fragmentManager.findFragmentByTag(TAG_DUPLICATES);
+ Fragment duplicatesUtilFragment =
+ fragmentManager.findFragmentByTag(TAG_DUPLICATES_UTIL);
+ if (duplicatesFragment == null || duplicatesUtilFragment == null) {
+ duplicatesFragment = ObjectFactory.getDuplicatesFragment();
+ duplicatesUtilFragment = ObjectFactory.getDuplicatesUtilFragment();
duplicatesUtilFragment.setTargetFragment(duplicatesFragment, /* requestCode */ 0);
- transaction.replace(
- R.id.contacts_list_container, duplicatesFragment, TAG_DUPLICATES);
- transaction.add(duplicatesUtilFragment, TAG_DUPLICATES_UTIL);
}
+ transaction.replace(
+ R.id.contacts_list_container, duplicatesFragment, TAG_DUPLICATES);
+ if (!duplicatesUtilFragment.isAdded()) {
+ transaction.add(duplicatesUtilFragment, TAG_DUPLICATES_UTIL);
+ resetToolBarStatusBarColor();
+ }
+ resetToolBarStatusBarColor();
}
transaction.addToBackStack(TAG_SECOND_LEVEL);
transaction.commit();
@@ -806,6 +816,7 @@
mShouldSwitchToAllContacts = false;
mCurrentView = ContactsView.ALL_CONTACTS;
showFabWithAnimation(/* showFab */ true);
+ mAllFragment.scrollToTop();
super.switchToAllContacts();
}
@@ -814,6 +825,14 @@
getFragmentManager().popBackStackImmediate(
TAG_SECOND_LEVEL, FragmentManager.POP_BACK_STACK_INCLUSIVE);
mMembersFragment = null;
+ resetToolBarStatusBarColor();
+ }
+
+ // Reset toolbar and status bar color to Contacts theme color.
+ private void resetToolBarStatusBarColor() {
+ findViewById(R.id.toolbar_frame).setBackgroundColor(
+ ContextCompat.getColor(this, R.color.primary_color));
+ updateStatusBarBackground(ContextCompat.getColor(this, R.color.primary_color_dark));
}
@Override
diff --git a/src/com/android/contacts/common/util/MaterialColorMapUtils.java b/src/com/android/contacts/common/util/MaterialColorMapUtils.java
index a8fbf42..50d8f0f 100644
--- a/src/com/android/contacts/common/util/MaterialColorMapUtils.java
+++ b/src/com/android/contacts/common/util/MaterialColorMapUtils.java
@@ -16,13 +16,16 @@
package com.android.contacts.common.util;
-import com.android.contacts.common.R;
+import com.android.contacts.ContactsDrawerActivity;
+import com.android.contacts.R;
+import android.app.Activity;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Trace;
+import android.support.v4.content.ContextCompat;
public class MaterialColorMapUtils {
private final TypedArray sPrimaryColors;
@@ -177,4 +180,26 @@
return H;
}
+
+ /**
+ * Returns status bar color for group view and non-group views.
+ */
+ public static int getStatusBarColor(Activity activity) {
+ final boolean isGroupView = activity instanceof ContactsDrawerActivity
+ && ((ContactsDrawerActivity) activity).isGroupView();
+ return isGroupView
+ ? ContextCompat.getColor(activity, R.color.group_primary_color_dark)
+ : ContextCompat.getColor(activity, R.color.primary_color_dark);
+ }
+
+ /**
+ * Returns toolbar color for group view and non-group views.
+ */
+ public static int getToolBarColor(Activity activity) {
+ final boolean isGroupView = activity instanceof ContactsDrawerActivity
+ && ((ContactsDrawerActivity) activity).isGroupView();
+ return isGroupView
+ ? ContextCompat.getColor(activity, R.color.group_primary_color)
+ : ContextCompat.getColor(activity, R.color.primary_color);
+ }
}
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 80e1b82..0542436 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -203,6 +203,13 @@
public static final String INTENT_EXTRA_PHOTO_ID = "photo_id";
/**
+ * Intent key to pass the ID of the raw contact id that should be displayed in the full editor
+ * by itself.
+ */
+ public static final String INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE =
+ "raw_contact_id_to_display_alone";
+
+ /**
* Intent extra to specify a {@link ContactEditor.SaveMode}.
*/
public static final String SAVE_MODE_EXTRA_KEY = "saveMode";
@@ -1478,6 +1485,8 @@
mIntentExtras.getInt(INTENT_EXTRA_MATERIAL_PALETTE_PRIMARY_COLOR),
mIntentExtras.getInt(INTENT_EXTRA_MATERIAL_PALETTE_SECONDARY_COLOR));
}
+ mRawContactIdToDisplayAlone = mIntentExtras
+ .getLong(INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE);
}
}
diff --git a/src/com/android/contacts/editor/EditorIntents.java b/src/com/android/contacts/editor/EditorIntents.java
index c4f48e6..c903b84 100644
--- a/src/com/android/contacts/editor/EditorIntents.java
+++ b/src/com/android/contacts/editor/EditorIntents.java
@@ -24,6 +24,7 @@
import android.text.TextUtils;
import com.android.contacts.activities.ContactEditorActivity;
+import com.android.contacts.activities.ContactEditorSpringBoardActivity;
import com.android.contacts.common.model.RawContactDeltaList;
import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
@@ -38,19 +39,32 @@
}
/**
- * Returns an Intent to start the {@link ContactEditorActivity} for an
+ * Returns an Intent to start the {@link ContactEditorSpringBoardActivity} for an
* existing contact.
*/
- public static Intent createEditContactIntent(Context context, Uri contactLookupUri,
+ public static Intent createEditContactIntent(Context context, Uri uri,
MaterialPalette materialPalette, long photoId) {
- final Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri, context,
- ContactEditorActivity.class);
+ final Intent intent = new Intent(Intent.ACTION_EDIT, uri, context,
+ ContactEditorSpringBoardActivity.class);
putMaterialPalette(intent, materialPalette);
putPhotoId(intent, photoId);
return intent;
}
/**
+ * Returns an Intent to start the {@link ContactEditorActivity} for the given raw contact.
+ */
+ public static Intent createEditContactIntentForRawContact(Context context,
+ Uri uri, long rawContactId, MaterialPalette materialPalette) {
+ final Intent intent = new Intent(Intent.ACTION_EDIT, uri, context,
+ ContactEditorActivity.class);
+ intent.putExtra(ContactEditorFragment.INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE,
+ rawContactId);
+ putMaterialPalette(intent, materialPalette);
+ return intent;
+ }
+
+ /**
* Returns an Intent to start the {@link ContactEditorActivity} for a new contact with
* the field values specified by rawContactDeltaList pre-populate in the form.
*/
@@ -71,9 +85,9 @@
* Returns an Intent to edit a different contact in the editor with whatever
* values were already entered on the current editor.
*/
- public static Intent createEditOtherContactIntent(Context context, Uri contactLookupUri,
+ public static Intent createEditOtherContactIntent(Context context, Uri uri,
ArrayList<ContentValues> contentValues) {
- final Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri, context,
+ final Intent intent = new Intent(Intent.ACTION_EDIT, uri, context,
ContactEditorActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_FORWARD_RESULT);
diff --git a/src/com/android/contacts/editor/PickRawContactDialogFragment.java b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
new file mode 100644
index 0000000..20e8f35
--- /dev/null
+++ b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
@@ -0,0 +1,162 @@
+package com.android.contacts.editor;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.contacts.R;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+
+/**
+ * Dialog containing the raw contacts that make up a contact. On selection the editor is loaded
+ * for the chosen raw contact.
+ */
+public class PickRawContactDialogFragment extends DialogFragment {
+ /**
+ * Used to list the account info for the given raw contacts list.
+ */
+ private static final class RawContactAccountListAdapter extends CursorAdapter {
+ private final LayoutInflater mInflater;
+ private final Context mContext;
+
+ public RawContactAccountListAdapter(Context context, Cursor cursor) {
+ super(context, cursor, 0);
+ mContext = context;
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ final long rawContactId = cursor.getLong(PickRawContactLoader.RAW_CONTACT_ID);
+ final String accountName = cursor.getString(PickRawContactLoader.ACCOUNT_NAME);
+ final String accountType = cursor.getString(PickRawContactLoader.ACCOUNT_TYPE);
+ final String dataSet = cursor.getString(PickRawContactLoader.DATA_SET);
+ final AccountType account = AccountTypeManager.getInstance(mContext)
+ .getAccountType(accountType, dataSet);
+
+ final ContactsPreferences prefs = new ContactsPreferences(mContext);
+ final int displayNameColumn =
+ prefs.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY
+ ? PickRawContactLoader.DISPLAY_NAME_PRIMARY
+ : PickRawContactLoader.DISPLAY_NAME_ALTERNATIVE;
+ String displayName = cursor.getString(displayNameColumn);
+
+ final TextView nameView = (TextView) view.findViewById(
+ R.id.display_name);
+ final TextView accountTextView = (TextView) view.findViewById(
+ R.id.account_name);
+ final ImageView accountIconView = (ImageView) view.findViewById(
+ R.id.account_icon);
+
+ if (!account.areContactsWritable()) {
+ displayName = mContext
+ .getString(R.string.contact_editor_pick_raw_contact_read_only, displayName);
+ view.setAlpha(.38f);
+ } else {
+ view.setAlpha(1f);
+ }
+
+ nameView.setText(displayName);
+ accountTextView.setText(accountName);
+ accountIconView.setImageDrawable(account.getDisplayIcon(mContext));
+
+ final ContactPhotoManager.DefaultImageRequest
+ request = new ContactPhotoManager.DefaultImageRequest(
+ displayName, String.valueOf(rawContactId), /* isCircular = */ true);
+ final ImageView photoView = (ImageView) view.findViewById(
+ R.id.photo);
+ ContactPhotoManager.getInstance(mContext).loadDirectoryPhoto(photoView,
+ ContactPhotoManager.getDefaultAvatarUriForContact(request),
+ /* darkTheme = */ false,
+ /* isCircular = */ true,
+ request);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.raw_contact_list_item, parent, false);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ getCursor().moveToPosition(position);
+ return getCursor().getLong(PickRawContactLoader.RAW_CONTACT_ID);
+ }
+ }
+
+ // Cursor holding all raw contact rows for the given Contact.
+ private Cursor mCursor;
+ // Uri for the whole Contact.
+ private Uri mUri;
+ private MaterialPalette mMaterialPalette;
+
+ public static PickRawContactDialogFragment getInstance(Uri uri, Cursor cursor,
+ MaterialPalette materialPalette) {
+ final PickRawContactDialogFragment fragment = new PickRawContactDialogFragment();
+ fragment.setUri(uri);
+ fragment.setCursor(cursor);
+ fragment.setMaterialPalette(materialPalette);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ final CursorAdapter adapter = new RawContactAccountListAdapter(getContext(), mCursor);
+ builder.setTitle(R.string.contact_editor_pick_raw_contact_dialog_title);
+ builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final long rawContactId = adapter.getItemId(which);
+ final Intent intent = EditorIntents.createEditContactIntentForRawContact(
+ getActivity(), mUri, rawContactId, mMaterialPalette);
+ intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ ImplicitIntentsUtil.startActivityInApp(getActivity(), intent);
+ }
+ });
+ builder.setCancelable(true);
+ return builder.create();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ mCursor = null;
+ finishActivity();
+ }
+
+ private void setUri(Uri uri) {
+ mUri = uri;
+ }
+
+ private void setCursor(Cursor cursor) {
+ mCursor = cursor;
+ }
+
+ private void setMaterialPalette(MaterialPalette materialPalette) {
+ mMaterialPalette = materialPalette;
+ }
+
+ private void finishActivity() {
+ if (getActivity() != null && !getActivity().isFinishing()) {
+ getActivity().finish();
+ }
+ }
+}
diff --git a/src/com/android/contacts/editor/PickRawContactLoader.java b/src/com/android/contacts/editor/PickRawContactLoader.java
new file mode 100644
index 0000000..62be517
--- /dev/null
+++ b/src/com/android/contacts/editor/PickRawContactLoader.java
@@ -0,0 +1,78 @@
+package com.android.contacts.editor;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+
+/**
+ * Loader for the pick a raw contact to edit activity. Loads all raw contact metadata for the
+ * given Contact {@link Uri}.
+ */
+public class PickRawContactLoader extends CursorLoader {
+ private Uri mContactUri;
+
+ public static final String[] COLUMNS = new String[] {
+ RawContacts.ACCOUNT_NAME,
+ RawContacts.ACCOUNT_TYPE,
+ RawContacts.DATA_SET,
+ RawContacts._ID,
+ RawContacts.DISPLAY_NAME_PRIMARY,
+ RawContacts.DISPLAY_NAME_ALTERNATIVE
+ };
+
+ public static final String SELECTION = RawContacts.CONTACT_ID + "=?";
+
+ public static final int ACCOUNT_NAME = 0;
+ public static final int ACCOUNT_TYPE = 1;
+ public static final int DATA_SET = 2;
+ public static final int RAW_CONTACT_ID = 3;
+ public static final int DISPLAY_NAME_PRIMARY = 4;
+ public static final int DISPLAY_NAME_ALTERNATIVE = 5;
+
+ public PickRawContactLoader(Context context, Uri contactUri) {
+ super(context, ensureIsContactUri(contactUri), COLUMNS, SELECTION, null, RawContacts._ID);
+ mContactUri = contactUri;
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+ // Get the id of the contact we're looking at.
+ final Cursor cursor = getContext().getContentResolver()
+ .query(mContactUri, new String[] { Contacts._ID }, null,
+ null, null);
+
+ if (cursor == null) {
+ return null;
+ }
+
+ if (cursor.getCount() < 1) {
+ cursor.close();
+ return null;
+ }
+
+ cursor.moveToFirst();
+ final long contactId = cursor.getLong(0);
+ cursor.close();
+ // Update selection arguments and uri.
+ setSelectionArgs(new String[]{ Long.toString(contactId) });
+ setUri(RawContacts.CONTENT_URI);
+ return super.loadInBackground();
+ }
+
+ /**
+ * Ensures that this is a valid contact URI. If invalid, then an exception is
+ * thrown. Otherwise, the original URI is returned.
+ */
+ private static Uri ensureIsContactUri(final Uri uri) {
+ if (uri == null) {
+ throw new IllegalArgumentException("Uri must not be null");
+ }
+ if (!uri.toString().startsWith(Contacts.CONTENT_URI.toString())) {
+ throw new IllegalArgumentException("Invalid contact Uri: " + uri);
+ }
+ return uri;
+ }
+}
diff --git a/src/com/android/contacts/group/GroupMembersFragment.java b/src/com/android/contacts/group/GroupMembersFragment.java
index 809cf20..30f339c 100644
--- a/src/com/android/contacts/group/GroupMembersFragment.java
+++ b/src/com/android/contacts/group/GroupMembersFragment.java
@@ -24,8 +24,9 @@
import android.database.CursorWrapper;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
import android.provider.ContactsContract.Contacts;
-import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -78,7 +79,7 @@
private static final String ARG_GROUP_URI = "groupUri";
private static final int LOADER_GROUP_METADATA = 0;
-
+ private static final int MSG_FAIL_TO_LOAD = 1;
private static final int RESULT_GROUP_ADD_MEMBER = 100;
/** Filters out duplicate contacts. */
@@ -194,9 +195,7 @@
Log.e(TAG, "Failed to load group metadata for " + mGroupUri);
Toast.makeText(getContext(), R.string.groupLoadErrorToast, Toast.LENGTH_SHORT)
.show();
- // TODO: we probably shouldn't finish mActivity.
- mActivity.setResult(AppCompatActivity.RESULT_CANCELED);
- mActivity.finish();
+ mHandler.sendEmptyMessage(MSG_FAIL_TO_LOAD);
return;
}
mGroupMetaData = new GroupMetaData(getActivity(), cursor);
@@ -219,6 +218,15 @@
private Set<String> mGroupMemberContactIds = new HashSet();
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if(msg.what == MSG_FAIL_TO_LOAD) {
+ mActivity.onBackPressed();
+ }
+ }
+ };
+
public static GroupMembersFragment newInstance(Uri groupUri) {
final Bundle args = new Bundle();
args.putParcelable(ARG_GROUP_URI, groupUri);
@@ -646,6 +654,13 @@
return mGroupMetaData != null && mGroupMetaData.groupId == groupId;
}
+ /**
+ * Return true if the fragment is not yet added, being removed, or detached.
+ */
+ public boolean isInactive() {
+ return !isAdded() || isRemoving() || isDetached();
+ }
+
@Override
public void onDestroy() {
if (mActionBarAdapter != null) {
diff --git a/tests/src/com/android/contacts/NoPermissionsLaunchSmokeTest.java b/tests/src/com/android/contacts/NoPermissionsLaunchSmokeTest.java
index a196ffa..8364b7b 100644
--- a/tests/src/com/android/contacts/NoPermissionsLaunchSmokeTest.java
+++ b/tests/src/com/android/contacts/NoPermissionsLaunchSmokeTest.java
@@ -3,6 +3,7 @@
import android.Manifest;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
@@ -75,6 +76,12 @@
device.wait(Until.hasObject(By.textEndsWith("make and manage phone calls?")), TIMEOUT);
+ final PackageManager packageManager = mTargetContext.getPackageManager();
+ if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ device.waitForIdle();
+ return;
+ }
+
final UiObject2 grantPhonePermissionButton = device.findObject(By.text("ALLOW"));
grantPhonePermissionButton.clickAndWait(Until.newWindow(), TIMEOUT);