Merge "Order strequent contacts above type-to-filter search results" into ub-contactsdialer-b-dev
diff --git a/src/com/android/contacts/common/activity/AppCompatPreferenceActivity.java b/src/com/android/contacts/common/activity/AppCompatPreferenceActivity.java
new file mode 100644
index 0000000..c3b7e94
--- /dev/null
+++ b/src/com/android/contacts/common/activity/AppCompatPreferenceActivity.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 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.common.activity;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatDelegate;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ */
+public class AppCompatPreferenceActivity extends PreferenceActivity {
+    private AppCompatDelegate mDelegate;
+
+    private boolean mIsSafeToCommitTransactions;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        getDelegate().installViewFactory();
+        getDelegate().onCreate(savedInstanceState);
+        super.onCreate(savedInstanceState);
+        mIsSafeToCommitTransactions = true;
+    }
+
+    @Override
+    protected void onPostCreate(Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+        getDelegate().onPostCreate(savedInstanceState);
+    }
+
+    public ActionBar getSupportActionBar() {
+        return getDelegate().getSupportActionBar();
+    }
+
+    public void setSupportActionBar(Toolbar toolbar) {
+        getDelegate().setSupportActionBar(toolbar);
+    }
+
+    @Override
+    public MenuInflater getMenuInflater() {
+        return getDelegate().getMenuInflater();
+    }
+
+    @Override
+    public void setContentView(int layoutResID) {
+        getDelegate().setContentView(layoutResID);
+    }
+
+    @Override
+    public void setContentView(View view) {
+        getDelegate().setContentView(view);
+    }
+
+    @Override
+    public void setContentView(View view, ViewGroup.LayoutParams params) {
+        getDelegate().setContentView(view, params);
+    }
+
+    @Override
+    public void addContentView(View view, ViewGroup.LayoutParams params) {
+        getDelegate().addContentView(view, params);
+    }
+
+    @Override
+    protected void onPostResume() {
+        super.onPostResume();
+        getDelegate().onPostResume();
+    }
+
+    @Override
+    protected void onTitleChanged(CharSequence title, int color) {
+        super.onTitleChanged(title, color);
+        getDelegate().setTitle(title);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        getDelegate().onConfigurationChanged(newConfig);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        getDelegate().onStop();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        getDelegate().onDestroy();
+    }
+
+    @Override
+    public void invalidateOptionsMenu() {
+        getDelegate().invalidateOptionsMenu();
+    }
+
+    private AppCompatDelegate getDelegate() {
+        if (mDelegate == null) {
+            mDelegate = AppCompatDelegate.create(this, null);
+        }
+        return mDelegate;
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mIsSafeToCommitTransactions = true;
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mIsSafeToCommitTransactions = true;
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        mIsSafeToCommitTransactions = false;
+    }
+
+    /**
+     * Returns true if it is safe to commit {@link FragmentTransaction}s at this time, based on
+     * whether {@link Activity#onSaveInstanceState} has been called or not.
+     *
+     * Make sure that the current activity calls into
+     * {@link super.onSaveInstanceState(Bundle outState)} (if that method is overridden),
+     * so the flag is properly set.
+     */
+    public boolean isSafeToCommitTransactions() {
+        return mIsSafeToCommitTransactions;
+    }
+}
diff --git a/src/com/android/contacts/common/activity/LicenseActivity.java b/src/com/android/contacts/common/activity/LicenseActivity.java
index d72b305..9e86ee8 100644
--- a/src/com/android/contacts/common/activity/LicenseActivity.java
+++ b/src/com/android/contacts/common/activity/LicenseActivity.java
@@ -17,7 +17,8 @@
 
 import com.android.contacts.common.R;
 
-import android.app.Activity;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
 import android.os.Bundle;
 import android.view.MenuItem;
 import android.webkit.WebView;
@@ -25,7 +26,7 @@
 /**
  * Displays the licenses for all open source libraries.
  */
-public class LicenseActivity extends Activity {
+public class LicenseActivity extends AppCompatActivity {
     private static final String LICENSE_FILE = "file:///android_asset/licenses.html";
     private WebView mWebView;
 
@@ -35,6 +36,10 @@
         setContentView(R.layout.licenses);
         mWebView = (WebView) findViewById(R.id.webview);
         mWebView.loadUrl(LICENSE_FILE);
+        final ActionBar actionBar = getSupportActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
+        }
     }
 
     @Override
diff --git a/src/com/android/contacts/common/compat/TelephonyManagerCompat.java b/src/com/android/contacts/common/compat/TelephonyManagerCompat.java
index 0f55c48..ec7907f 100644
--- a/src/com/android/contacts/common/compat/TelephonyManagerCompat.java
+++ b/src/com/android/contacts/common/compat/TelephonyManagerCompat.java
@@ -16,10 +16,13 @@
 
 package com.android.contacts.common.compat;
 
-import android.content.Context;
+import android.net.Uri;
 import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
 import android.telephony.TelephonyManager;
 
+import com.android.contacts.common.ContactsUtils;
+
 public class TelephonyManagerCompat {
     public static final String TELEPHONY_MANAGER_CLASS = "android.telephony.TelephonyManager";
 
@@ -131,4 +134,40 @@
         }
         return false;
     }
+
+    /**
+     * Returns the URI for the per-account voicemail ringtone set in Phone settings.
+     *
+     * @param telephonyManager The telephony manager instance to use for method calls.
+     * @param accountHandle The handle for the {@link android.telecom.PhoneAccount} for which to
+     * retrieve the voicemail ringtone.
+     * @return The URI for the ringtone to play when receiving a voicemail from a specific
+     * PhoneAccount.
+     */
+    @Nullable
+    public static Uri getVoicemailRingtoneUri(TelephonyManager telephonyManager,
+            PhoneAccountHandle accountHandle) {
+        if (!CompatUtils.isNCompatible()) {
+            return null;
+        }
+        return TelephonyManagerSdkCompat
+                .getVoicemailRingtoneUri(telephonyManager, accountHandle);
+    }
+
+    /**
+     * Returns whether vibration is set for voicemail notification in Phone settings.
+     *
+     * @param telephonyManager The telephony manager instance to use for method calls.
+     * @param accountHandle The handle for the {@link android.telecom.PhoneAccount} for which to
+     * retrieve the voicemail vibration setting.
+     * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
+     */
+    public static boolean isVoicemailVibrationEnabled(TelephonyManager telephonyManager,
+            PhoneAccountHandle accountHandle) {
+        if (!CompatUtils.isNCompatible()) {
+            return true;
+        }
+        return TelephonyManagerSdkCompat
+                .isVoicemailVibrationEnabled(telephonyManager, accountHandle);
+    }
 }
diff --git a/src/com/android/contacts/common/preference/AboutPreferenceFragment.java b/src/com/android/contacts/common/preference/AboutPreferenceFragment.java
new file mode 100644
index 0000000..3ab32d5
--- /dev/null
+++ b/src/com/android/contacts/common/preference/AboutPreferenceFragment.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package com.android.contacts.common.preference;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.activity.LicenseActivity;
+
+/**
+ * This fragment shows the preferences for "about".
+ */
+public class AboutPreferenceFragment extends PreferenceFragment {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Load the preferences from an XML resource
+        addPreferencesFromResource(R.xml.preference_about);
+
+        // Set build version of Contacts App.
+        final PackageManager manager = getActivity().getPackageManager();
+        try {
+            final PackageInfo info = manager.getPackageInfo(getActivity().getPackageName(), 0);
+            final Preference versionPreference = findPreference(
+                    getString(R.string.pref_build_version_key));
+            versionPreference.setSummary(info.versionName);
+        } catch (PackageManager.NameNotFoundException e) {
+            // Nothing
+        }
+
+        final Preference licensePreference = findPreference(
+                getString(R.string.pref_open_source_licenses_key));
+        licensePreference.setIntent(new Intent(getActivity(), LicenseActivity.class));
+    }
+
+    @Override
+    public Context getContext() {
+        return getActivity();
+    }
+}
+
diff --git a/src/com/android/contacts/common/preference/ContactsPreferenceActivity.java b/src/com/android/contacts/common/preference/ContactsPreferenceActivity.java
new file mode 100644
index 0000000..3fd10d8
--- /dev/null
+++ b/src/com/android/contacts/common/preference/ContactsPreferenceActivity.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2009 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.common.preference;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.view.MenuItem;
+
+import com.android.contacts.common.activity.AppCompatPreferenceActivity;
+import com.android.contacts.common.R;
+
+/**
+ * Contacts settings.
+ */
+public final class ContactsPreferenceActivity extends AppCompatPreferenceActivity {
+
+    private static final String TAG_ABOUT_CONTACTS = "about_contacts";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final ActionBar actionBar = getSupportActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
+        }
+
+        if (savedInstanceState == null) {
+            getFragmentManager().beginTransaction()
+                    .replace(android.R.id.content, new DisplayOptionsPreferenceFragment())
+                    .commit();
+            setActivityTitle(R.string.activity_title_settings);
+        } else {
+            final AboutPreferenceFragment fragment = (AboutPreferenceFragment) getFragmentManager()
+                    .findFragmentByTag(TAG_ABOUT_CONTACTS);
+            setActivityTitle(fragment == null ?
+                    R.string.activity_title_settings : R.string.setting_about);
+        }
+    }
+
+    public void showAboutFragment() {
+        getFragmentManager().beginTransaction()
+                .replace(android.R.id.content, new AboutPreferenceFragment(), TAG_ABOUT_CONTACTS)
+                .addToBackStack(null)
+                .commit();
+        setActivityTitle(R.string.setting_about);
+    }
+
+    /**
+     * Returns true if there are no preferences to display and therefore the
+     * corresponding menu item can be removed.
+     */
+    public static boolean isEmpty(Context context) {
+        return !context.getResources().getBoolean(R.bool.config_sort_order_user_changeable)
+                && !context.getResources().getBoolean(R.bool.config_display_order_user_changeable)
+                && !context.getResources().getBoolean(
+                        R.bool.config_default_account_user_changeable);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            onBackPressed();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (getFragmentManager().getBackStackEntryCount() > 0) {
+            setActivityTitle(R.string.activity_title_settings);
+            getFragmentManager().popBackStack();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    private void setActivityTitle(int res) {
+        final ActionBar actionBar = getSupportActionBar();
+        if (actionBar != null) {
+            actionBar.setTitle(res);
+        }
+    }
+}
diff --git a/src/com/android/contacts/common/preference/ContactsPreferences.java b/src/com/android/contacts/common/preference/ContactsPreferences.java
index 37448e3..5815ef0 100644
--- a/src/com/android/contacts/common/preference/ContactsPreferences.java
+++ b/src/com/android/contacts/common/preference/ContactsPreferences.java
@@ -72,7 +72,7 @@
 
     public static final boolean PREF_DISPLAY_ONLY_PHONES_DEFAULT = false;
 
-    public static final String DO_NOT_SYNC_CONTACT_METADATA_MSG = "Do not sync contact metadata.";
+    public static final String DO_NOT_SYNC_CONTACT_METADATA_MSG = "Do not sync metadata";
 
     public static final String CONTACT_METADATA_AUTHORITY = "com.android.contacts.metadata";
 
diff --git a/src/com/android/contacts/common/preference/DisplayOptionsPreferenceFragment.java b/src/com/android/contacts/common/preference/DisplayOptionsPreferenceFragment.java
index a8d1460..6f91616 100644
--- a/src/com/android/contacts/common/preference/DisplayOptionsPreferenceFragment.java
+++ b/src/com/android/contacts/common/preference/DisplayOptionsPreferenceFragment.java
@@ -17,25 +17,21 @@
 package com.android.contacts.common.preference;
 
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.os.Bundle;
-import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
 
 import com.android.contacts.common.R;
-import com.android.contacts.common.activity.LicenseActivity;
+import com.android.contacts.common.compat.MetadataSyncEnabledCompat;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.GoogleAccountType;
 
 import java.util.List;
 
 /**
- * This fragment shows the preferences for the first header.
+ * This fragment shows the preferences for "display options"
  */
 public class DisplayOptionsPreferenceFragment extends PreferenceFragment {
 
@@ -48,20 +44,14 @@
 
         removeUnsupportedPreferences();
 
-        // Set build version of Contacts App.
-        final PackageManager manager = getActivity().getPackageManager();
-        try {
-            final PackageInfo info = manager.getPackageInfo(getActivity().getPackageName(), 0);
-            final Preference versionPreference = findPreference(
-                    getString(R.string.pref_build_version_key));
-            versionPreference.setSummary(info.versionName);
-        } catch (PackageManager.NameNotFoundException e) {
-            // Nothing
-        }
-
-        final Preference licensePreference = findPreference(
-                getString(R.string.pref_open_source_licenses_key));
-        licensePreference.setIntent(new Intent(getActivity(), LicenseActivity.class));
+        final Preference aboutPreference = findPreference("about");
+        aboutPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+            @Override
+            public boolean onPreferenceClick(Preference preference) {
+                ((ContactsPreferenceActivity) getActivity()).showAboutFragment();
+                return true;
+            }
+        });
     }
 
     private void removeUnsupportedPreferences() {
@@ -84,9 +74,19 @@
             getPreferenceScreen().removePreference(findPreference("accounts"));
         }
 
-        // STOPSHIP Show this option when 1) metadata sync is enabled
+        // Show Contact metadata sync option when 1) metadata sync is enabled
         // and 2) there is at least one focus google account
-        getPreferenceScreen().removePreference(findPreference("contactMetadata"));
+        boolean hasFocusGoogleAccount = false;
+        for (AccountWithDataSet account : accounts) {
+            if (GoogleAccountType.ACCOUNT_TYPE.equals(account.type) && account.dataSet == null) {
+                hasFocusGoogleAccount = true;
+                break;
+            }
+        }
+        if (!hasFocusGoogleAccount
+                || !MetadataSyncEnabledCompat.isMetadataSyncEnabled(getContext())) {
+            getPreferenceScreen().removePreference(findPreference("contactMetadata"));
+        }
     }
 
     @Override