/* * 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.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.states.RotationHelper; import com.android.launcher3.util.DisplayController; 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 (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 BuildConfig.NOTIFICATION_DOTS_ENABLED; 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; } 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; } } }