Dialer: Fix settings

* RingtonePreference doesn't exist in androidx world - we have to
  make our own
* We don't need to remove the dividers anymore - androidx doesn't have
  them in the first place
* AppCompatPreferenceActivity is deprecated as well, make it
  an AppCompatActivity and start using fragments

Change-Id: I54d22868deb8950618b69652ae3810b8e0bcdb80
diff --git a/java/com/android/dialer/app/res/layout/activity_settings.xml b/java/com/android/dialer/app/res/layout/activity_settings.xml
new file mode 100644
index 0000000..e53ce21
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/activity_settings.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/content_frame" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/java/com/android/dialer/app/settings/AppCompatPreferenceActivity.java b/java/com/android/dialer/app/settings/AppCompatPreferenceActivity.java
deleted file mode 100644
index ee8c424..0000000
--- a/java/com/android/dialer/app/settings/AppCompatPreferenceActivity.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * Copyright (C) 2023 The LineageOS 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.dialer.app.settings;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.preference.PreferenceActivity;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.appcompat.app.AppCompatDelegate;
-
-/**
- * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
- * to be used with AppCompat.
- */
-public class AppCompatPreferenceActivity extends PreferenceActivity {
-
-  private AppCompatDelegate delegate;
-
-  private boolean isSafeToCommitTransactions;
-
-  @Override
-  protected void onCreate(Bundle savedInstanceState) {
-    getDelegate().installViewFactory();
-    getDelegate().onCreate(savedInstanceState);
-    super.onCreate(savedInstanceState);
-    isSafeToCommitTransactions = true;
-  }
-
-  @Override
-  protected void onPostCreate(Bundle savedInstanceState) {
-    super.onPostCreate(savedInstanceState);
-    getDelegate().onPostCreate(savedInstanceState);
-  }
-
-  @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 (delegate == null) {
-      delegate = AppCompatDelegate.create(this, null);
-    }
-    return delegate;
-  }
-
-  @Override
-  protected void onStart() {
-    super.onStart();
-    isSafeToCommitTransactions = true;
-  }
-
-  @Override
-  protected void onResume() {
-    super.onResume();
-    isSafeToCommitTransactions = true;
-  }
-
-  @Override
-  protected void onSaveInstanceState(Bundle outState) {
-    super.onSaveInstanceState(outState);
-    isSafeToCommitTransactions = 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.
-   *
-   * <p>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 isSafeToCommitTransactions;
-  }
-}
diff --git a/java/com/android/dialer/app/settings/DefaultRingtonePreference.java b/java/com/android/dialer/app/settings/DefaultRingtonePreference.java
index 50989ce..cbabb74 100644
--- a/java/com/android/dialer/app/settings/DefaultRingtonePreference.java
+++ b/java/com/android/dialer/app/settings/DefaultRingtonePreference.java
@@ -21,32 +21,44 @@
 import android.content.Intent;
 import android.media.RingtoneManager;
 import android.net.Uri;
-import android.preference.RingtonePreference;
 import android.provider.Settings;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.widget.Toast;
 
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultCallback;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+
 import com.android.dialer.R;
 
 /** RingtonePreference which doesn't show default ringtone setting. */
-public class DefaultRingtonePreference extends RingtonePreference {
+public class DefaultRingtonePreference extends Preference {
+
+  private final Intent mRingtonePickerIntent;
 
   public DefaultRingtonePreference(Context context, AttributeSet attrs) {
     super(context, attrs);
+
+    mRingtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+    mRingtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, getRingtoneType());
+    mRingtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
+    mRingtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+    mRingtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
+            Settings.System.DEFAULT_NOTIFICATION_URI);
+    mRingtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, getTitle());
   }
 
-  @Override
-  protected void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
-    super.onPrepareRingtonePickerIntent(ringtonePickerIntent);
-
-    /*
-     * Since this preference is for choosing the default ringtone, it
-     * doesn't make sense to show a 'Default' item.
-     */
-    ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
+  public Intent getRingtonePickerIntent() {
+    mRingtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
+            onRestoreRingtone());
+    return mRingtonePickerIntent;
   }
 
-  @Override
   protected void onSaveRingtone(Uri ringtoneUri) {
     if (!Settings.System.canWrite(getContext())) {
       Toast.makeText(
@@ -59,8 +71,11 @@
     RingtoneManager.setActualDefaultRingtoneUri(getContext(), getRingtoneType(), ringtoneUri);
   }
 
-  @Override
   protected Uri onRestoreRingtone() {
     return RingtoneManager.getActualDefaultRingtoneUri(getContext(), getRingtoneType());
   }
+
+  private int getRingtoneType() {
+    return RingtoneManager.TYPE_RINGTONE;
+  }
 }
diff --git a/java/com/android/dialer/app/settings/DialerPreferenceFragment.java b/java/com/android/dialer/app/settings/DialerPreferenceFragment.java
deleted file mode 100644
index d2af4de..0000000
--- a/java/com/android/dialer/app/settings/DialerPreferenceFragment.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SPDX-FileCopyrightText: The LineageOS Project
- * SPDX-License-Identifier: Apache-2.0
- */
-package com.android.dialer.app.settings;
-
-import android.os.Bundle;
-import android.view.View;
-import android.widget.ListView;
-
-import androidx.annotation.Nullable;
-import androidx.preference.PreferenceFragmentCompat;
-
-public class DialerPreferenceFragment extends PreferenceFragmentCompat {
-
-    @Override
-    public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
-
-    }
-
-    @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
-        ListView lv = (ListView) view.findViewById(android.R.id.list);
-        if (lv != null) {
-            lv.setDivider(null);
-        }
-    }
-}
diff --git a/java/com/android/dialer/app/settings/DialerSettingsActivity.java b/java/com/android/dialer/app/settings/DialerSettingsActivity.java
index 5e08ed9..ec7d85e 100644
--- a/java/com/android/dialer/app/settings/DialerSettingsActivity.java
+++ b/java/com/android/dialer/app/settings/DialerSettingsActivity.java
@@ -16,6 +16,7 @@
  */
 package com.android.dialer.app.settings;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -28,30 +29,29 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
-import android.view.MenuItem;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceManager;
 
-import com.android.dialer.app.R;
+import com.android.dialer.R;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.compat.telephony.TelephonyManagerCompat;
 import com.android.dialer.lookup.LookupSettingsFragment;
-import com.android.dialer.proguard.UsedByReflection;
 import com.android.dialer.util.PermissionsUtil;
 import com.android.dialer.voicemail.settings.VoicemailSettingsFragment;
 import com.android.voicemail.VoicemailClient;
 
-import java.util.List;
-
 /** Activity for dialer settings. */
-@SuppressWarnings("FragmentInjection") // Activity not exported
-@UsedByReflection(value = "AndroidManifest-app.xml")
-public class DialerSettingsActivity extends AppCompatPreferenceActivity {
+public class DialerSettingsActivity extends AppCompatActivity implements
+        PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
 
   protected SharedPreferences preferences;
-  private List<Header> headers;
 
   @Override
   protected void onCreate(Bundle savedInstanceState) {
@@ -59,225 +59,241 @@
     super.onCreate(savedInstanceState);
     preferences = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext());
 
+    setContentView(R.layout.activity_settings);
+
     Intent intent = getIntent();
     Uri data = intent.getData();
+
+    String initialFragment = null;
     if (data != null) {
       String headerToOpen = data.getSchemeSpecificPart();
-      if (headerToOpen != null && headers != null) {
-        for (Header header : headers) {
-          if (headerToOpen.equals(header.fragment)) {
-            LogUtil.i("DialerSettingsActivity.onCreate", "switching to header: " + headerToOpen);
-            switchToHeader(header);
-            break;
-          }
-        }
+      if (headerToOpen != null) {
+        initialFragment = headerToOpen;
       }
     }
-  }
 
-  @Override
-  protected void onResume() {
-    super.onResume();
-  }
-
-  @Override
-  public void onBuildHeaders(List<Header> target) {
-    // Keep a reference to the list of headers (since PreferenceActivity.getHeaders() is @Hide)
-    headers = target;
-
-    if (showDisplayOptions()) {
-      Header displayOptionsHeader = new Header();
-      displayOptionsHeader.titleRes = R.string.display_options_title;
-      displayOptionsHeader.fragment = DisplayOptionsSettingsFragment.class.getName();
-      target.add(displayOptionsHeader);
-    }
-
-    Header soundSettingsHeader = new Header();
-    soundSettingsHeader.titleRes = R.string.sounds_and_vibration_title;
-    soundSettingsHeader.fragment = SoundSettingsFragment.class.getName();
-    soundSettingsHeader.id = R.id.settings_header_sounds_and_vibration;
-    target.add(soundSettingsHeader);
-
-    Header quickResponseSettingsHeader = new Header();
-    Intent quickResponseSettingsIntent =
-        new Intent(TelecomManager.ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS);
-    quickResponseSettingsHeader.titleRes = R.string.respond_via_sms_setting_title;
-    quickResponseSettingsHeader.intent = quickResponseSettingsIntent;
-    target.add(quickResponseSettingsHeader);
-
-    final Header lookupSettingsHeader = new Header();
-    lookupSettingsHeader.titleRes = R.string.lookup_settings_label;
-    lookupSettingsHeader.fragment = LookupSettingsFragment.class.getName();
-    target.add(lookupSettingsHeader);
-
-    TelephonyManager telephonyManager =
-        (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
-
-    // "Call Settings" (full settings) is shown if the current user is primary user and there
-    // is only one SIM. Otherwise, "Calling accounts" is shown.
-    boolean isPrimaryUser = isPrimaryUser();
-    if (isPrimaryUser && TelephonyManagerCompat.getPhoneCount(telephonyManager) <= 1) {
-      Header callSettingsHeader = new Header();
-      Intent callSettingsIntent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
-      callSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
-      callSettingsHeader.titleRes = R.string.call_settings_label;
-      callSettingsHeader.intent = callSettingsIntent;
-      target.add(callSettingsHeader);
-    } else {
-      Header phoneAccountSettingsHeader = new Header();
-      Intent phoneAccountSettingsIntent = new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
-      phoneAccountSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
-      phoneAccountSettingsHeader.titleRes = R.string.phone_account_settings_label;
-      phoneAccountSettingsHeader.intent = phoneAccountSettingsIntent;
-      target.add(phoneAccountSettingsHeader);
-    }
-    if (BlockedNumberContract.canCurrentUserBlockNumbers(this)) {
-      Header blockedCallsHeader = new Header();
-      blockedCallsHeader.titleRes = R.string.manage_blocked_numbers_label;
-      blockedCallsHeader.intent = getApplicationContext().getSystemService(TelecomManager.class)
-              .createManageBlockedNumbersIntent();
-      target.add(blockedCallsHeader);
-    }
-
-    addVoicemailSettings(target, isPrimaryUser);
-
-    if (isPrimaryUser
-        && (TelephonyManagerCompat.isTtyModeSupported(telephonyManager)
-            || TelephonyManagerCompat.isHearingAidCompatibilitySupported(telephonyManager))) {
-      Header accessibilitySettingsHeader = new Header();
-      Intent accessibilitySettingsIntent =
-          new Intent(TelecomManager.ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS);
-      accessibilitySettingsHeader.titleRes = R.string.accessibility_settings_title;
-      accessibilitySettingsHeader.intent = accessibilitySettingsIntent;
-      target.add(accessibilitySettingsHeader);
-    }
-  }
-
-  private void addVoicemailSettings(List<Header> target, boolean isPrimaryUser) {
-    if (!isPrimaryUser) {
-      LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "user not primary user");
-      return;
-    }
-
-    if (!PermissionsUtil.hasReadPhoneStatePermissions(this)) {
-      LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "Missing READ_PHONE_STATE");
-      return;
-    }
-
-    LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "adding voicemail settings");
-    Header voicemailSettings = new Header();
-    voicemailSettings.titleRes = R.string.voicemail_settings_label;
-    PhoneAccountHandle soleAccount = getSoleSimAccount();
-    if (soleAccount == null) {
-      LogUtil.i(
-          "DialerSettingsActivity.addVoicemailSettings", "showing multi-SIM voicemail settings");
-      voicemailSettings.fragment = PhoneAccountSelectionFragment.class.getName();
-      Bundle bundle = new Bundle();
-      bundle.putString(
-          PhoneAccountSelectionFragment.PARAM_TARGET_FRAGMENT,
-          VoicemailSettingsFragment.class.getName());
-      bundle.putString(
-          PhoneAccountSelectionFragment.PARAM_PHONE_ACCOUNT_HANDLE_KEY,
-          VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE);
-      bundle.putBundle(PhoneAccountSelectionFragment.PARAM_ARGUMENTS, new Bundle());
-      bundle.putInt(
-          PhoneAccountSelectionFragment.PARAM_TARGET_TITLE_RES, R.string.voicemail_settings_label);
-      voicemailSettings.fragmentArguments = bundle;
-      target.add(voicemailSettings);
-    } else {
-      LogUtil.i(
-          "DialerSettingsActivity.addVoicemailSettings", "showing single-SIM voicemail settings");
-      voicemailSettings.fragment = VoicemailSettingsFragment.class.getName();
-      Bundle bundle = new Bundle();
-      bundle.putParcelable(VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE, soleAccount);
-      voicemailSettings.fragmentArguments = bundle;
-      target.add(voicemailSettings);
-    }
-  }
-
-  /**
-   * @return the only SIM phone account, or {@code null} if there are none or more than one. Note:
-   *     having a empty SIM slot still count as a PhoneAccountHandle that is "invalid", and
-   *     voicemail settings should still be available for it.
-   */
-  @Nullable
-  private PhoneAccountHandle getSoleSimAccount() {
-    TelecomManager telecomManager = getSystemService(TelecomManager.class);
-    PhoneAccountHandle result = null;
-    for (PhoneAccountHandle phoneAccountHandle : telecomManager.getCallCapablePhoneAccounts()) {
-      PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
-      if (phoneAccount == null) {
-        continue;
+    // If savedInstanceState is non-null, then the activity is being
+    // recreated and super.onCreate() has already recreated the fragment.
+    if (savedInstanceState == null) {
+      if (initialFragment == null) {
+        initialFragment = PrefsFragment.class.getName();
       }
-      if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
-        LogUtil.i(
-            "DialerSettingsActivity.getSoleSimAccount", phoneAccountHandle + " is a SIM account");
-        if (result != null) {
-          return null;
-        }
-        result = phoneAccountHandle;
+      Fragment fragment;
+      try {
+        fragment = getSupportFragmentManager()
+                .getFragmentFactory()
+                .instantiate(getClassLoader(), initialFragment);
+      } catch (Exception ignored) {
+        fragment = new PrefsFragment();
       }
+
+      getSupportFragmentManager().beginTransaction()
+              .replace(R.id.content_frame, fragment)
+              .commit();
     }
-    return result;
-  }
 
-  /**
-   * Returns {@code true} or {@code false} based on whether the display options setting should be
-   * shown. For languages such as Chinese, Japanese, or Korean, display options aren't useful since
-   * contacts are sorted and displayed family name first by default.
-   *
-   * @return {@code true} if the display options should be shown, {@code false} otherwise.
-   */
-  private boolean showDisplayOptions() {
-    return getResources().getBoolean(R.bool.config_display_order_user_changeable)
-        && getResources().getBoolean(R.bool.config_sort_order_user_changeable);
-  }
-
-  @Override
-  public void onHeaderClick(Header header, int position) {
-    if (header.id == R.id.settings_header_sounds_and_vibration) {
-      // If we don't have the permission to write to system settings, go to system sound
-      // settings instead. Otherwise, perform the super implementation (which launches our
-      // own preference fragment.
-      if (!Settings.System.canWrite(this)) {
-        Toast.makeText(
-                this,
-                getResources().getString(R.string.toast_cannot_write_system_settings),
-                Toast.LENGTH_SHORT)
-            .show();
-        startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
-        return;
+    getSupportFragmentManager().addOnBackStackChangedListener(() -> {
+      if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
+        setTitle(R.string.dialer_settings_label);
       }
-    }
-    super.onHeaderClick(header, position);
+    });
   }
 
   @Override
-  public boolean onOptionsItemSelected(MenuItem item) {
-    if (item.getItemId() == android.R.id.home) {
-      onBackPressed();
-      return true;
-    }
-    return false;
-  }
-
-  @Override
-  public void onBackPressed() {
-    if (!isSafeToCommitTransactions()) {
-      return;
-    }
-    super.onBackPressed();
-  }
-
-  @Override
-  protected boolean isValidFragment(String fragmentName) {
+  public boolean onPreferenceStartFragment(@NonNull PreferenceFragmentCompat caller,
+                                           @NonNull Preference pref) {
+    Fragment fragment = getSupportFragmentManager()
+            .getFragmentFactory()
+            .instantiate(getClassLoader(), pref.getFragment());
+    fragment.setArguments(pref.getExtras());
+    getSupportFragmentManager().beginTransaction()
+            .replace(R.id.content_frame, fragment, "")
+            .addToBackStack(null)
+            .commit();
+    setTitle(pref.getTitle());
     return true;
   }
 
-  /** @return Whether the current user is the primary user. */
-  private boolean isPrimaryUser() {
-    return getSystemService(UserManager.class).isSystemUser();
+  public static class PrefsFragment extends PreferenceFragmentCompat implements
+          Preference.OnPreferenceClickListener {
+
+    @Override
+    public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
+      setPreferenceScreen(getPreferenceManager().createPreferenceScreen(requireContext()));
+
+      if (showDisplayOptions()) {
+        Preference displayOptions = new Preference(getContext());
+        displayOptions.setTitle(R.string.display_options_title);
+        displayOptions.setFragment(DisplayOptionsSettingsFragment.class.getName());
+        getPreferenceScreen().addPreference(displayOptions);
+      }
+
+      Preference soundSettings = new Preference(getContext());
+      soundSettings.setTitle(R.string.sounds_and_vibration_title);
+      soundSettings.setFragment(SoundSettingsFragment.class.getName());
+      soundSettings.setViewId(R.id.settings_header_sounds_and_vibration);
+      getPreferenceScreen().addPreference(soundSettings);
+
+      Preference quickResponseSettings = new Preference(getContext());
+      Intent quickResponseSettingsIntent =
+              new Intent(TelecomManager.ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS);
+      quickResponseSettings.setTitle(R.string.respond_via_sms_setting_title);
+      quickResponseSettings.setIntent(quickResponseSettingsIntent);
+      getPreferenceScreen().addPreference(quickResponseSettings);
+
+      final Preference lookupSettings = new Preference(getContext());
+      lookupSettings.setTitle(R.string.lookup_settings_label);
+      lookupSettings.setFragment(LookupSettingsFragment.class.getName());
+      getPreferenceScreen().addPreference(lookupSettings);
+
+      TelephonyManager telephonyManager = getContext().getSystemService(TelephonyManager.class);
+      TelecomManager telecomManager = (TelecomManager) getContext().getSystemService(
+              Context.TELECOM_SERVICE);
+
+      // "Call Settings" (full settings) is shown if the current user is primary user and there
+      // is only one SIM. Otherwise, "Calling accounts" is shown.
+      boolean isPrimaryUser = isPrimaryUser();
+      if (isPrimaryUser && TelephonyManagerCompat.getPhoneCount(telephonyManager) <= 1) {
+        Preference callSettings = new Preference(getContext());
+        Intent callSettingsIntent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
+        callSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+        callSettings.setTitle(R.string.call_settings_label);
+        callSettings.setIntent(callSettingsIntent);
+        getPreferenceScreen().addPreference(callSettings);
+      } else {
+        Preference phoneAccountSettings = new Preference(getContext());
+        Intent phoneAccountSettingsIntent = new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
+        phoneAccountSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+        phoneAccountSettings.setTitle(R.string.phone_account_settings_label);
+        phoneAccountSettings.setIntent(phoneAccountSettingsIntent);
+        getPreferenceScreen().addPreference(phoneAccountSettings);
+      }
+      if (BlockedNumberContract.canCurrentUserBlockNumbers(getContext())) {
+        Preference blockedCalls = new Preference(getContext());
+        blockedCalls.setTitle(R.string.manage_blocked_numbers_label);
+        blockedCalls.setIntent(getContext().getSystemService(TelecomManager.class)
+                .createManageBlockedNumbersIntent());
+        getPreferenceScreen().addPreference(blockedCalls);
+      }
+
+      addVoicemailSettings(isPrimaryUser);
+
+      if (isPrimaryUser
+              && (TelephonyManagerCompat.isTtyModeSupported(telecomManager)
+              || TelephonyManagerCompat.isHearingAidCompatibilitySupported(telephonyManager))) {
+        Preference accessibilitySettings = new Preference(getContext());
+        Intent accessibilitySettingsIntent =
+                new Intent(TelecomManager.ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS);
+        accessibilitySettings.setTitle(R.string.accessibility_settings_title);
+        accessibilitySettings.setIntent(accessibilitySettingsIntent);
+        getPreferenceScreen().addPreference(accessibilitySettings);
+      }
+    }
+
+    @Override
+    public boolean onPreferenceClick(@NonNull Preference preference) {
+      if (preference.getTitle().equals(getString(R.string.sounds_and_vibration_title))) {
+        // If we don't have the permission to write to system settings, go to system sound
+        // settings instead. Otherwise, perform the super implementation (which launches our
+        // own preference fragment.
+        if (!Settings.System.canWrite(getContext())) {
+          Toast.makeText(getContext(),
+                          getResources().getString(R.string.toast_cannot_write_system_settings),
+                          Toast.LENGTH_SHORT)
+                  .show();
+          startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
+          return true;
+        }
+      }
+      super.onPreferenceTreeClick(preference);
+      return false;
+    }
+
+    private void addVoicemailSettings(boolean isPrimaryUser) {
+      if (!isPrimaryUser) {
+        LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "user not primary user");
+        return;
+      }
+
+      if (!PermissionsUtil.hasReadPhoneStatePermissions(getContext())) {
+        LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "Missing READ_PHONE_STATE");
+        return;
+      }
+
+      LogUtil.i("DialerSettingsActivity.addVoicemailSettings", "adding voicemail settings");
+      Preference voicemailSettings = new Preference(getContext());
+      voicemailSettings.setTitle(R.string.voicemail_settings_label);
+      Bundle bundle = new Bundle();
+      PhoneAccountHandle soleAccount = getSoleSimAccount();
+      if (soleAccount == null) {
+        LogUtil.i("DialerSettingsActivity.addVoicemailSettings",
+                "showing multi-SIM voicemail settings");
+        voicemailSettings.setFragment(PhoneAccountSelectionFragment.class.getName());
+        bundle.putString(
+                PhoneAccountSelectionFragment.PARAM_TARGET_FRAGMENT,
+                VoicemailSettingsFragment.class.getName());
+        bundle.putString(
+                PhoneAccountSelectionFragment.PARAM_PHONE_ACCOUNT_HANDLE_KEY,
+                VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE);
+        bundle.putBundle(PhoneAccountSelectionFragment.PARAM_ARGUMENTS, new Bundle());
+        bundle.putInt(PhoneAccountSelectionFragment.PARAM_TARGET_TITLE_RES,
+                R.string.voicemail_settings_label);
+      } else {
+        LogUtil.i(
+                "DialerSettingsActivity.addVoicemailSettings", "showing single-SIM voicemail settings");
+        voicemailSettings.setFragment(VoicemailSettingsFragment.class.getName());
+        bundle.putParcelable(VoicemailClient.PARAM_PHONE_ACCOUNT_HANDLE, soleAccount);
+      }
+      voicemailSettings.getExtras().putAll(bundle);
+      getPreferenceScreen().addPreference(voicemailSettings);
+    }
+
+    /**
+     * @return the only SIM phone account, or {@code null} if there are none or more than one. Note:
+     * having a empty SIM slot still count as a PhoneAccountHandle that is "invalid", and
+     * voicemail settings should still be available for it.
+     */
+    @SuppressLint("MissingPermission")
+    @Nullable
+    private PhoneAccountHandle getSoleSimAccount() {
+      TelecomManager telecomManager = requireContext().getSystemService(TelecomManager.class);
+      PhoneAccountHandle result = null;
+      for (PhoneAccountHandle phoneAccountHandle : telecomManager.getCallCapablePhoneAccounts()) {
+        PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
+        if (phoneAccount == null) {
+          continue;
+        }
+        if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+          LogUtil.i(
+                  "DialerSettingsActivity.getSoleSimAccount", phoneAccountHandle + " is a SIM account");
+          if (result != null) {
+            return null;
+          }
+          result = phoneAccountHandle;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Returns {@code true} or {@code false} based on whether the display options setting should be
+     * shown. For languages such as Chinese, Japanese, or Korean, display options aren't useful since
+     * contacts are sorted and displayed family name first by default.
+     *
+     * @return {@code true} if the display options should be shown, {@code false} otherwise.
+     */
+    private boolean showDisplayOptions() {
+      return getResources().getBoolean(R.bool.config_display_order_user_changeable)
+              && getResources().getBoolean(R.bool.config_sort_order_user_changeable);
+    }
+
+    /**
+     * @return Whether the current user is the primary user.
+     */
+    private boolean isPrimaryUser() {
+      return requireContext().getSystemService(UserManager.class).isSystemUser();
+    }
   }
 }
diff --git a/java/com/android/dialer/app/settings/DisplayOptionsSettingsFragment.java b/java/com/android/dialer/app/settings/DisplayOptionsSettingsFragment.java
index 4a3e545..93853ae 100644
--- a/java/com/android/dialer/app/settings/DisplayOptionsSettingsFragment.java
+++ b/java/com/android/dialer/app/settings/DisplayOptionsSettingsFragment.java
@@ -20,10 +20,11 @@
 import android.os.Bundle;
 
 import androidx.annotation.Nullable;
+import androidx.preference.PreferenceFragmentCompat;
 
 import com.android.dialer.R;
 
-public class DisplayOptionsSettingsFragment extends DialerPreferenceFragment {
+public class DisplayOptionsSettingsFragment extends PreferenceFragmentCompat {
 
   @Override
   public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
diff --git a/java/com/android/dialer/app/settings/PhoneAccountSelectionFragment.java b/java/com/android/dialer/app/settings/PhoneAccountSelectionFragment.java
index 28b1bfd..01073b3 100644
--- a/java/com/android/dialer/app/settings/PhoneAccountSelectionFragment.java
+++ b/java/com/android/dialer/app/settings/PhoneAccountSelectionFragment.java
@@ -17,27 +17,29 @@
 
 package com.android.dialer.app.settings;
 
-import android.app.Fragment;
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
-import android.preference.PreferenceActivity;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 
 import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceScreen;
 
+import com.android.dialer.R;
+
 import java.util.List;
 
 /**
  * Preference screen that lists SIM phone accounts to select from, and forwards the selected account
- * to {@link #PARAM_TARGET_FRAGMENT}. Can only be used in a {@link PreferenceActivity}
+ * to {@link #PARAM_TARGET_FRAGMENT}.
  */
-public class PhoneAccountSelectionFragment extends DialerPreferenceFragment {
+public class PhoneAccountSelectionFragment extends PreferenceFragmentCompat {
 
   /** The {@link PreferenceFragmentCompat} to launch after the account is selected. */
   public static final String PARAM_TARGET_FRAGMENT = "target_fragment";
@@ -45,7 +47,7 @@
   /**
    * The arguments bundle to pass to the {@link #PARAM_TARGET_FRAGMENT}
    *
-   * @see Fragment#getArguments()
+   * @see PreferenceFragmentCompat#getArguments()
    */
   public static final String PARAM_ARGUMENTS = "arguments";
 
@@ -55,8 +57,7 @@
   public static final String PARAM_PHONE_ACCOUNT_HANDLE_KEY = "phone_account_handle_key";
 
   /**
-   * The title of the {@link #PARAM_TARGET_FRAGMENT} once it is launched with {@link
-   * PreferenceActivity#startWithFragment(String, Bundle, Fragment, int)}, as a string resource ID.
+   * The title of the {@link #PARAM_TARGET_FRAGMENT} once it is launched, as a string resource ID.
    */
   public static final String PARAM_TARGET_TITLE_RES = "target_title_res";
 
@@ -98,10 +99,16 @@
     @Override
     protected void onClick() {
       super.onClick();
-      // TODO BadDaemon: fix this
-      /*PreferenceActivity preferenceActivity = (PreferenceActivity) getActivity();
+      Fragment fragment = getChildFragmentManager()
+              .getFragmentFactory()
+              .instantiate(getActivity().getClassLoader(), targetFragment);
       arguments.putParcelable(phoneAccountHandleKey, phoneAccountHandle);
-      preferenceActivity.startWithFragment(targetFragment, arguments, null, 0, titleRes, 0);*/
+      fragment.setArguments(arguments);
+      getParentFragmentManager().beginTransaction()
+              .replace(R.id.content_frame, fragment)
+              .addToBackStack(null)
+              .commit();
+      setTitle(titleRes);
     }
   }
 
diff --git a/java/com/android/dialer/app/settings/SoundSettingsFragment.java b/java/com/android/dialer/app/settings/SoundSettingsFragment.java
index 8e12149..2148f2b 100644
--- a/java/com/android/dialer/app/settings/SoundSettingsFragment.java
+++ b/java/com/android/dialer/app/settings/SoundSettingsFragment.java
@@ -17,13 +17,18 @@
 
 package com.android.dialer.app.settings;
 
+import static android.app.Activity.RESULT_OK;
+
+import android.annotation.SuppressLint;
 import android.app.AlertDialog;
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.Intent;
 import android.media.RingtoneManager;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -31,9 +36,12 @@
 import android.telephony.TelephonyManager;
 import android.widget.Toast;
 
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.Nullable;
 import androidx.preference.ListPreference;
 import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreferenceCompat;
 
@@ -41,7 +49,7 @@
 import com.android.dialer.callrecord.impl.CallRecorderService;
 import com.android.dialer.util.SettingsUtil;
 
-public class SoundSettingsFragment extends DialerPreferenceFragment
+public class SoundSettingsFragment extends PreferenceFragmentCompat
     implements Preference.OnPreferenceChangeListener {
 
   private static final int NO_DTMF_TONE = 0;
@@ -54,7 +62,7 @@
 
   private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1;
 
-  private Preference ringtonePreference;
+  private DefaultRingtonePreference ringtonePreference;
   private final Handler ringtoneLookupComplete =
       new Handler(Looper.getMainLooper()) {
         @Override
@@ -68,6 +76,18 @@
       };
   private final Runnable ringtoneLookupRunnable = () -> updateRingtonePreferenceSummary();
 
+  private final ActivityResultLauncher<Intent> mRingtonePickerResult = registerForActivityResult(
+          new ActivityResultContracts.StartActivityForResult(), result -> {
+            if (result.getResultCode() == RESULT_OK) {
+              Intent data = result.getData();
+              if (data == null || data.getExtras() == null) {
+                return;
+              }
+              Uri uri = (Uri) data.getExtras().get(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+              ringtonePreference.onSaveRingtone(uri);
+            }
+          });
+
   private SwitchPreferenceCompat vibrateWhenRinging;
   private SwitchPreferenceCompat playDtmfTone;
   private ListPreference dtmfToneLength;
@@ -221,7 +241,9 @@
           .show();
       return true;
     }
-    if (preference == playDtmfTone) {
+    if (preference == ringtonePreference) {
+      mRingtonePickerResult.launch(ringtonePreference.getRingtonePickerIntent());
+    } else if (preference == playDtmfTone) {
       Settings.System.putInt(
           getActivity().getContentResolver(),
           Settings.System.DTMF_TONE_WHEN_DIALING,
@@ -249,7 +271,7 @@
   private boolean shouldVibrateWhenRinging() {
     int vibrateWhenRingingSetting =
         Settings.System.getInt(
-            getActivity().getContentResolver(),
+            requireActivity().getContentResolver(),
             Settings.System.VIBRATE_WHEN_RINGING,
             NO_VIBRATION_FOR_CALLS);
     return hasVibrator() && (vibrateWhenRingingSetting == DO_VIBRATION_FOR_CALLS);
@@ -259,7 +281,7 @@
   private boolean shouldPlayDtmfTone() {
     int dtmfToneSetting =
         Settings.System.getInt(
-            getActivity().getContentResolver(),
+            requireActivity().getContentResolver(),
             Settings.System.DTMF_TONE_WHEN_DIALING,
             PLAY_DTMF_TONE);
     return dtmfToneSetting == PLAY_DTMF_TONE;
@@ -267,13 +289,14 @@
 
   /** Whether the device hardware has a vibrator. */
   private boolean hasVibrator() {
-    Vibrator vibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
+    Vibrator vibrator = requireActivity().getSystemService(Vibrator.class);
     return vibrator != null && vibrator.hasVibrator();
   }
 
+  @SuppressLint("MissingPermission")
   private boolean shouldHideCarrierSettings() {
     CarrierConfigManager configManager =
-        (CarrierConfigManager) getActivity().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        (CarrierConfigManager) requireActivity().getSystemService(Context.CARRIER_CONFIG_SERVICE);
     return configManager
         .getConfig()
         .getBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL);
diff --git a/java/com/android/dialer/lookup/LookupSettingsFragment.java b/java/com/android/dialer/lookup/LookupSettingsFragment.java
index 490a5b7..970198e 100644
--- a/java/com/android/dialer/lookup/LookupSettingsFragment.java
+++ b/java/com/android/dialer/lookup/LookupSettingsFragment.java
@@ -23,12 +23,12 @@
 import androidx.annotation.Nullable;
 import androidx.preference.ListPreference;
 import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.SwitchPreferenceCompat;
 
 import com.android.dialer.R;
-import com.android.dialer.app.settings.DialerPreferenceFragment;
 
-public class LookupSettingsFragment extends DialerPreferenceFragment
+public class LookupSettingsFragment extends PreferenceFragmentCompat
     implements Preference.OnPreferenceChangeListener {
 
   private static final String KEY_ENABLE_FORWARD_LOOKUP = "enable_forward_lookup";
diff --git a/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
index 942f396..4c9f0ba 100644
--- a/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
+++ b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
@@ -30,12 +30,11 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.preference.Preference;
-import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreferenceCompat;
 
 import com.android.dialer.R;
-import com.android.dialer.app.settings.DialerPreferenceFragment;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.notification.NotificationChannelManager;
@@ -52,7 +51,7 @@
  * Fragment for voicemail settings. Requires {@link VoicemailClient#PARAM_PHONE_ACCOUNT_HANDLE} set
  * in arguments.
  */
-public class VoicemailSettingsFragment extends DialerPreferenceFragment
+public class VoicemailSettingsFragment extends PreferenceFragmentCompat
     implements Preference.OnPreferenceChangeListener, ActivationStateListener {
 
   // Extras copied from com.android.phone.settings.VoicemailSettingsActivity,
@@ -84,6 +83,11 @@
   }
 
   @Override
+  public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
+
+  }
+
+  @Override
   public void onResume() {
     super.onResume();
     voicemailClient.addActivationStateListener(this);