diff options
Diffstat (limited to 'java/com/android/dialer/app/settings')
5 files changed, 678 insertions, 0 deletions
diff --git a/java/com/android/dialer/app/settings/AppCompatPreferenceActivity.java b/java/com/android/dialer/app/settings/AppCompatPreferenceActivity.java new file mode 100644 index 000000000..2c464386b --- /dev/null +++ b/java/com/android/dialer/app/settings/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.dialer.app.settings; + +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. + * + * <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 mIsSafeToCommitTransactions; + } +} diff --git a/java/com/android/dialer/app/settings/DefaultRingtonePreference.java b/java/com/android/dialer/app/settings/DefaultRingtonePreference.java new file mode 100644 index 000000000..579584e0f --- /dev/null +++ b/java/com/android/dialer/app/settings/DefaultRingtonePreference.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 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.dialer.app.settings; + +import android.content.Context; +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.widget.Toast; +import com.android.dialer.app.R; + +/** RingtonePreference which doesn't show default ringtone setting. */ +public class DefaultRingtonePreference extends RingtonePreference { + + public DefaultRingtonePreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @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); + } + + @Override + protected void onSaveRingtone(Uri ringtoneUri) { + if (!Settings.System.canWrite(getContext())) { + Toast.makeText( + getContext(), + getContext().getResources().getString(R.string.toast_cannot_write_system_settings), + Toast.LENGTH_SHORT) + .show(); + return; + } + RingtoneManager.setActualDefaultRingtoneUri(getContext(), getRingtoneType(), ringtoneUri); + } + + @Override + protected Uri onRestoreRingtone() { + return RingtoneManager.getActualDefaultRingtoneUri(getContext(), getRingtoneType()); + } +} diff --git a/java/com/android/dialer/app/settings/DialerSettingsActivity.java b/java/com/android/dialer/app/settings/DialerSettingsActivity.java new file mode 100644 index 000000000..b04674013 --- /dev/null +++ b/java/com/android/dialer/app/settings/DialerSettingsActivity.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2013 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.dialer.app.settings; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.os.UserManager; +import android.preference.PreferenceManager; +import android.provider.Settings; +import android.telecom.TelecomManager; +import android.telephony.TelephonyManager; +import android.view.MenuItem; +import android.widget.Toast; +import com.android.contacts.common.compat.TelephonyManagerCompat; +import com.android.dialer.app.R; +import com.android.dialer.blocking.FilteredNumberCompat; +import com.android.dialer.compat.CompatUtils; +import com.android.dialer.proguard.UsedByReflection; +import java.util.List; + +@UsedByReflection(value = "AndroidManifest-app.xml") +public class DialerSettingsActivity extends AppCompatPreferenceActivity { + + protected SharedPreferences mPreferences; + private boolean migrationStatusOnBuildHeaders; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPreferences = PreferenceManager.getDefaultSharedPreferences(this); + } + + @Override + protected void onResume() { + super.onResume(); + /* + * The blockedCallsHeader need to be recreated if the migration status changed because + * the intent needs to be updated. + */ + if (migrationStatusOnBuildHeaders != FilteredNumberCompat.hasMigratedToNewBlocking(this)) { + invalidateHeaders(); + } + } + + @Override + public void onBuildHeaders(List<Header> 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); + + if (CompatUtils.isMarshmallowCompatible()) { + 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); + } + + 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. Before N, "Calling accounts" setting is shown if the current user is + // primary user and there are multiple SIMs. In N+, "Calling accounts" is shown whenever + // "Call Settings" is not 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 if ((VERSION.SDK_INT >= VERSION_CODES.N) || isPrimaryUser) { + 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 (FilteredNumberCompat.canCurrentUserOpenBlockSettings(this)) { + Header blockedCallsHeader = new Header(); + blockedCallsHeader.titleRes = R.string.manage_blocked_numbers_label; + blockedCallsHeader.intent = FilteredNumberCompat.createManageBlockedNumbersIntent(this); + target.add(blockedCallsHeader); + migrationStatusOnBuildHeaders = FilteredNumberCompat.hasMigratedToNewBlocking(this); + } + 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); + } + } + + /** + * 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; + } + } + 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) { + return true; + } + + /** @return Whether the current user is the primary user. */ + private boolean isPrimaryUser() { + return getSystemService(UserManager.class).isSystemUser(); + } +} diff --git a/java/com/android/dialer/app/settings/DisplayOptionsSettingsFragment.java b/java/com/android/dialer/app/settings/DisplayOptionsSettingsFragment.java new file mode 100644 index 000000000..bf1637f27 --- /dev/null +++ b/java/com/android/dialer/app/settings/DisplayOptionsSettingsFragment.java @@ -0,0 +1,30 @@ +/* + * 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.dialer.app.settings; + +import android.os.Bundle; +import android.preference.PreferenceFragment; +import com.android.dialer.app.R; + +public class DisplayOptionsSettingsFragment extends PreferenceFragment { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.display_options_settings); + } +} diff --git a/java/com/android/dialer/app/settings/SoundSettingsFragment.java b/java/com/android/dialer/app/settings/SoundSettingsFragment.java new file mode 100644 index 000000000..83ce45398 --- /dev/null +++ b/java/com/android/dialer/app/settings/SoundSettingsFragment.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2014 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.dialer.app.settings; + +import android.content.Context; +import android.media.RingtoneManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Vibrator; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.telephony.TelephonyManager; +import android.widget.Toast; +import com.android.dialer.app.R; +import com.android.dialer.compat.SdkVersionOverride; +import com.android.dialer.util.SettingsUtil; + +public class SoundSettingsFragment extends PreferenceFragment + implements Preference.OnPreferenceChangeListener { + + private static final int NO_DTMF_TONE = 0; + private static final int PLAY_DTMF_TONE = 1; + + private static final int NO_VIBRATION_FOR_CALLS = 0; + private static final int DO_VIBRATION_FOR_CALLS = 1; + + private static final int DTMF_TONE_TYPE_NORMAL = 0; + + private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1; + + private Preference mRingtonePreference; + private final Handler mRingtoneLookupComplete = + new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_RINGTONE_SUMMARY: + mRingtonePreference.setSummary((CharSequence) msg.obj); + break; + } + } + }; + private final Runnable mRingtoneLookupRunnable = + new Runnable() { + @Override + public void run() { + updateRingtonePreferenceSummary(); + } + }; + private CheckBoxPreference mVibrateWhenRinging; + private CheckBoxPreference mPlayDtmfTone; + private ListPreference mDtmfToneLength; + + @Override + public Context getContext() { + return getActivity(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.sound_settings); + + Context context = getActivity(); + + mRingtonePreference = findPreference(context.getString(R.string.ringtone_preference_key)); + mVibrateWhenRinging = + (CheckBoxPreference) findPreference(context.getString(R.string.vibrate_on_preference_key)); + mPlayDtmfTone = + (CheckBoxPreference) findPreference(context.getString(R.string.play_dtmf_preference_key)); + mDtmfToneLength = + (ListPreference) + findPreference(context.getString(R.string.dtmf_tone_length_preference_key)); + + if (hasVibrator()) { + mVibrateWhenRinging.setOnPreferenceChangeListener(this); + } else { + getPreferenceScreen().removePreference(mVibrateWhenRinging); + mVibrateWhenRinging = null; + } + + mPlayDtmfTone.setOnPreferenceChangeListener(this); + mPlayDtmfTone.setChecked(shouldPlayDtmfTone()); + + TelephonyManager telephonyManager = + (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); + if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M + && telephonyManager.canChangeDtmfToneLength() + && (telephonyManager.isWorldPhone() || !shouldHideCarrierSettings())) { + mDtmfToneLength.setOnPreferenceChangeListener(this); + mDtmfToneLength.setValueIndex( + Settings.System.getInt( + context.getContentResolver(), + Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, + DTMF_TONE_TYPE_NORMAL)); + } else { + getPreferenceScreen().removePreference(mDtmfToneLength); + mDtmfToneLength = null; + } + } + + @Override + public void onResume() { + super.onResume(); + + if (!Settings.System.canWrite(getContext())) { + // If the user launches this setting fragment, then toggles the WRITE_SYSTEM_SETTINGS + // AppOp, then close the fragment since there is nothing useful to do. + getActivity().onBackPressed(); + return; + } + + if (mVibrateWhenRinging != null) { + mVibrateWhenRinging.setChecked(shouldVibrateWhenRinging()); + } + + // Lookup the ringtone name asynchronously. + new Thread(mRingtoneLookupRunnable).start(); + } + + /** + * Supports onPreferenceChangeListener to look for preference changes. + * + * @param preference The preference to be changed + * @param objValue The value of the selection, NOT its localized display value. + */ + @Override + public boolean onPreferenceChange(Preference preference, Object objValue) { + if (!Settings.System.canWrite(getContext())) { + // A user shouldn't be able to get here, but this protects against monkey crashes. + Toast.makeText( + getContext(), + getResources().getString(R.string.toast_cannot_write_system_settings), + Toast.LENGTH_SHORT) + .show(); + return true; + } + if (preference == mVibrateWhenRinging) { + boolean doVibrate = (Boolean) objValue; + Settings.System.putInt( + getActivity().getContentResolver(), + Settings.System.VIBRATE_WHEN_RINGING, + doVibrate ? DO_VIBRATION_FOR_CALLS : NO_VIBRATION_FOR_CALLS); + } else if (preference == mDtmfToneLength) { + int index = mDtmfToneLength.findIndexOfValue((String) objValue); + Settings.System.putInt( + getActivity().getContentResolver(), Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, index); + } + return true; + } + + /** Click listener for toggle events. */ + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { + if (!Settings.System.canWrite(getContext())) { + Toast.makeText( + getContext(), + getResources().getString(R.string.toast_cannot_write_system_settings), + Toast.LENGTH_SHORT) + .show(); + return true; + } + if (preference == mPlayDtmfTone) { + Settings.System.putInt( + getActivity().getContentResolver(), + Settings.System.DTMF_TONE_WHEN_DIALING, + mPlayDtmfTone.isChecked() ? PLAY_DTMF_TONE : NO_DTMF_TONE); + } + return true; + } + + /** Updates the summary text on the ringtone preference with the name of the ringtone. */ + private void updateRingtonePreferenceSummary() { + SettingsUtil.updateRingtoneName( + getActivity(), + mRingtoneLookupComplete, + RingtoneManager.TYPE_RINGTONE, + mRingtonePreference.getKey(), + MSG_UPDATE_RINGTONE_SUMMARY); + } + + /** + * Obtain the value for "vibrate when ringing" setting. The default value is false. + * + * <p>Watch out: if the setting is missing in the device, this will try obtaining the old "vibrate + * on ring" setting from AudioManager, and save the previous setting to the new one. + */ + private boolean shouldVibrateWhenRinging() { + int vibrateWhenRingingSetting = + Settings.System.getInt( + getActivity().getContentResolver(), + Settings.System.VIBRATE_WHEN_RINGING, + NO_VIBRATION_FOR_CALLS); + return hasVibrator() && (vibrateWhenRingingSetting == DO_VIBRATION_FOR_CALLS); + } + + /** Obtains the value for dialpad/DTMF tones. The default value is true. */ + private boolean shouldPlayDtmfTone() { + int dtmfToneSetting = + Settings.System.getInt( + getActivity().getContentResolver(), + Settings.System.DTMF_TONE_WHEN_DIALING, + PLAY_DTMF_TONE); + return dtmfToneSetting == PLAY_DTMF_TONE; + } + + /** Whether the device hardware has a vibrator. */ + private boolean hasVibrator() { + Vibrator vibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE); + return vibrator != null && vibrator.hasVibrator(); + } + + private boolean shouldHideCarrierSettings() { + CarrierConfigManager configManager = + (CarrierConfigManager) getActivity().getSystemService(Context.CARRIER_CONFIG_SERVICE); + return configManager + .getConfig() + .getBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL); + } +} |