| /* |
| * 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.launcher3.settings; |
| |
| import static android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED; |
| |
| import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT; |
| |
| import static com.android.launcher3.BuildConfig.IS_DEBUG_DEVICE; |
| import static com.android.launcher3.BuildConfig.IS_STUDIO_BUILD; |
| import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY; |
| |
| import android.app.Activity; |
| import android.content.Intent; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.provider.Settings; |
| import android.text.TextUtils; |
| import android.view.MenuItem; |
| import android.view.View; |
| |
| import androidx.annotation.Nullable; |
| import androidx.annotation.VisibleForTesting; |
| import androidx.core.view.WindowCompat; |
| import androidx.fragment.app.DialogFragment; |
| import androidx.fragment.app.Fragment; |
| import androidx.fragment.app.FragmentActivity; |
| import androidx.fragment.app.FragmentManager; |
| import androidx.preference.Preference; |
| import androidx.preference.PreferenceCategory; |
| import androidx.preference.PreferenceFragmentCompat; |
| import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback; |
| import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartScreenCallback; |
| import androidx.preference.PreferenceGroup.PreferencePositionCallback; |
| import androidx.preference.PreferenceScreen; |
| import androidx.recyclerview.widget.RecyclerView; |
| |
| import com.android.launcher3.BuildConfig; |
| import com.android.launcher3.LauncherFiles; |
| import com.android.launcher3.R; |
| import com.android.launcher3.Utilities; |
| import com.android.launcher3.model.WidgetsModel; |
| import com.android.launcher3.states.RotationHelper; |
| import com.android.launcher3.uioverrides.flags.DeveloperOptionsUI; |
| import com.android.launcher3.util.DisplayController; |
| import com.android.launcher3.util.Executors; |
| import com.android.launcher3.util.SettingsCache; |
| |
| /** |
| * Settings activity for Launcher. Currently implements the following setting: Allow rotation |
| */ |
| public class SettingsActivity extends FragmentActivity |
| implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback { |
| |
| @VisibleForTesting |
| static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options"; |
| |
| private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging"; |
| |
| public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args"; |
| |
| // Intent extra to indicate the pref-key to highlighted when opening the settings activity |
| public static final String EXTRA_FRAGMENT_HIGHLIGHT_KEY = ":settings:fragment_args_key"; |
| // Intent extra to indicate the pref-key of the root screen when opening the settings activity |
| public static final String EXTRA_FRAGMENT_ROOT_KEY = ARG_PREFERENCE_ROOT; |
| |
| private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600; |
| public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted"; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.settings_activity); |
| |
| setActionBar(findViewById(R.id.action_bar)); |
| WindowCompat.setDecorFitsSystemWindows(getWindow(), false); |
| |
| Intent intent = getIntent(); |
| if (intent.hasExtra(EXTRA_FRAGMENT_ROOT_KEY) || intent.hasExtra(EXTRA_FRAGMENT_ARGS) |
| || intent.hasExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY)) { |
| getActionBar().setDisplayHomeAsUpEnabled(true); |
| } |
| |
| if (savedInstanceState == null) { |
| Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS); |
| if (args == null) { |
| args = new Bundle(); |
| } |
| |
| String highlight = intent.getStringExtra(EXTRA_FRAGMENT_HIGHLIGHT_KEY); |
| if (!TextUtils.isEmpty(highlight)) { |
| args.putString(EXTRA_FRAGMENT_HIGHLIGHT_KEY, highlight); |
| } |
| String root = intent.getStringExtra(EXTRA_FRAGMENT_ROOT_KEY); |
| if (!TextUtils.isEmpty(root)) { |
| args.putString(EXTRA_FRAGMENT_ROOT_KEY, root); |
| } |
| |
| final FragmentManager fm = getSupportFragmentManager(); |
| final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(), |
| getString(R.string.settings_fragment_name)); |
| f.setArguments(args); |
| // Display the fragment as the main content. |
| fm.beginTransaction().replace(R.id.content_frame, f).commit(); |
| } |
| } |
| |
| private boolean startPreference(String fragment, Bundle args, String key) { |
| if (Utilities.ATLEAST_P && getSupportFragmentManager().isStateSaved()) { |
| // Sometimes onClick can come after onPause because of being posted on the handler. |
| // Skip starting new preferences in that case. |
| return false; |
| } |
| final FragmentManager fm = getSupportFragmentManager(); |
| final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(), fragment); |
| if (f instanceof DialogFragment) { |
| f.setArguments(args); |
| ((DialogFragment) f).show(fm, key); |
| } else { |
| startActivity(new Intent(this, SettingsActivity.class) |
| .putExtra(EXTRA_FRAGMENT_ARGS, args)); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean onPreferenceStartFragment( |
| PreferenceFragmentCompat preferenceFragment, Preference pref) { |
| return startPreference(pref.getFragment(), pref.getExtras(), pref.getKey()); |
| } |
| |
| @Override |
| public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) { |
| Bundle args = new Bundle(); |
| args.putString(ARG_PREFERENCE_ROOT, pref.getKey()); |
| return startPreference(getString(R.string.settings_fragment_name), args, pref.getKey()); |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| if (item.getItemId() == android.R.id.home) { |
| onBackPressed(); |
| return true; |
| } |
| return super.onOptionsItemSelected(item); |
| } |
| |
| /** |
| * This fragment shows the launcher preferences. |
| */ |
| public static class LauncherSettingsFragment extends PreferenceFragmentCompat implements |
| SettingsCache.OnChangeListener { |
| |
| protected boolean mDeveloperOptionsEnabled = false; |
| |
| private boolean mRestartOnResume = false; |
| |
| private String mHighLightKey; |
| private boolean mPreferenceHighlighted = false; |
| |
| @Override |
| public void onCreate(@Nullable Bundle savedInstanceState) { |
| if (BuildConfig.IS_DEBUG_DEVICE) { |
| Uri devUri = Settings.Global.getUriFor(DEVELOPMENT_SETTINGS_ENABLED); |
| SettingsCache settingsCache = SettingsCache.INSTANCE.get(getContext()); |
| mDeveloperOptionsEnabled = settingsCache.getValue(devUri); |
| settingsCache.register(devUri, this); |
| } |
| super.onCreate(savedInstanceState); |
| } |
| |
| @Override |
| public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { |
| final Bundle args = getArguments(); |
| mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_HIGHLIGHT_KEY); |
| |
| if (savedInstanceState != null) { |
| mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY); |
| } |
| |
| getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY); |
| setPreferencesFromResource(R.xml.launcher_preferences, rootKey); |
| |
| PreferenceScreen screen = getPreferenceScreen(); |
| for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) { |
| Preference preference = screen.getPreference(i); |
| if (!initPreference(preference)) { |
| screen.removePreference(preference); |
| } |
| } |
| |
| if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) { |
| getActivity().setTitle(getPreferenceScreen().getTitle()); |
| } |
| } |
| |
| @Override |
| public void onViewCreated(View view, Bundle savedInstanceState) { |
| super.onViewCreated(view, savedInstanceState); |
| View listView = getListView(); |
| final int bottomPadding = listView.getPaddingBottom(); |
| listView.setOnApplyWindowInsetsListener((v, insets) -> { |
| v.setPadding( |
| v.getPaddingLeft(), |
| v.getPaddingTop(), |
| v.getPaddingRight(), |
| bottomPadding + insets.getSystemWindowInsetBottom()); |
| return insets.consumeSystemWindowInsets(); |
| }); |
| |
| // Overriding Text Direction in the Androidx preference library to support RTL |
| view.setTextDirection(View.TEXT_DIRECTION_LOCALE); |
| } |
| |
| @Override |
| public void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted); |
| } |
| |
| /** |
| * Initializes a preference. This is called for every preference. Returning false here |
| * will remove that preference from the list. |
| */ |
| protected boolean initPreference(Preference preference) { |
| switch (preference.getKey()) { |
| case NOTIFICATION_DOTS_PREFERENCE_KEY: |
| return !WidgetsModel.GO_DISABLE_NOTIFICATION_DOTS; |
| |
| case ALLOW_ROTATION_PREFERENCE_KEY: |
| DisplayController.Info info = |
| DisplayController.INSTANCE.get(getContext()).getInfo(); |
| if (info.isTablet(info.realBounds)) { |
| // Launcher supports rotation by default. No need to show this setting. |
| return false; |
| } |
| // Initialize the UI once |
| preference.setDefaultValue(RotationHelper.getAllowRotationDefaultValue(info)); |
| return true; |
| |
| case DEVELOPER_OPTIONS_KEY: |
| if (IS_STUDIO_BUILD) { |
| preference.setOrder(0); |
| } |
| return mDeveloperOptionsEnabled; |
| case "pref_developer_flags": |
| if (mDeveloperOptionsEnabled && preference instanceof PreferenceCategory pc) { |
| Executors.MAIN_EXECUTOR.post(() -> new DeveloperOptionsUI(this, pc)); |
| return true; |
| } |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| |
| if (isAdded() && !mPreferenceHighlighted) { |
| PreferenceHighlighter highlighter = createHighlighter(); |
| if (highlighter != null) { |
| getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS); |
| mPreferenceHighlighted = true; |
| } |
| } |
| |
| if (mRestartOnResume) { |
| recreateActivityNow(); |
| } |
| } |
| |
| @Override |
| public void onSettingsChanged(boolean isEnabled) { |
| // Developer options changed, try recreate |
| tryRecreateActivity(); |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| if (IS_DEBUG_DEVICE) { |
| SettingsCache.INSTANCE.get(getContext()) |
| .unregister(Settings.Global.getUriFor(DEVELOPMENT_SETTINGS_ENABLED), this); |
| } |
| } |
| |
| /** |
| * Tries to recreate the preference |
| */ |
| protected void tryRecreateActivity() { |
| if (isResumed()) { |
| recreateActivityNow(); |
| } else { |
| mRestartOnResume = true; |
| } |
| } |
| |
| private void recreateActivityNow() { |
| Activity activity = getActivity(); |
| if (activity != null) { |
| activity.recreate(); |
| } |
| } |
| |
| private PreferenceHighlighter createHighlighter() { |
| if (TextUtils.isEmpty(mHighLightKey)) { |
| return null; |
| } |
| |
| PreferenceScreen screen = getPreferenceScreen(); |
| if (screen == null) { |
| return null; |
| } |
| |
| RecyclerView list = getListView(); |
| PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter(); |
| int position = callback.getPreferenceAdapterPosition(mHighLightKey); |
| return position >= 0 ? new PreferenceHighlighter( |
| list, position, screen.findPreference(mHighLightKey)) |
| : null; |
| } |
| } |
| } |