/* * 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 androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS; import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.text.TextUtils; import android.view.MenuItem; import android.view.View; import androidx.annotation.NonNull; 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.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherFiles; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.states.RotationHelper; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import java.util.Collections; import java.util.List; /** * Settings activity for Launcher. Currently implements the following setting: Allow rotation */ public class SettingsActivity extends FragmentActivity implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback, SharedPreferences.OnSharedPreferenceChangeListener{ /** List of fragments that can be hosted by this activity. */ private static final List VALID_PREFERENCE_FRAGMENTS = !Utilities.IS_DEBUG_DEVICE ? Collections.emptyList() : Collections.singletonList(DeveloperOptionsFragment.class.getName()); private static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options"; private static final String FLAGS_PREFERENCE_KEY = "flag_toggler"; private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging"; public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; public static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args"; private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600; public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted"; @VisibleForTesting static final String EXTRA_FRAGMENT = ":settings:fragment"; @VisibleForTesting static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args"; @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) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)) { getActionBar().setDisplayHomeAsUpEnabled(true); } if (savedInstanceState == null) { Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS); if (args == null) { args = new Bundle(); } String prefKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY); if (!TextUtils.isEmpty(prefKey)) { args.putString(EXTRA_FRAGMENT_ARG_KEY, prefKey); } final FragmentManager fm = getSupportFragmentManager(); final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(), getPreferenceFragment()); f.setArguments(args); // Display the fragment as the main content. fm.beginTransaction().replace(R.id.content_frame, f).commit(); } Utilities.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this); } /** * Obtains the preference fragment to instantiate in this activity. * * @return the preference fragment class * @throws IllegalArgumentException if the fragment is unknown to this activity */ private String getPreferenceFragment() { String preferenceFragment = getIntent().getStringExtra(EXTRA_FRAGMENT); String defaultFragment = getString(R.string.settings_fragment_name); if (TextUtils.isEmpty(preferenceFragment)) { return defaultFragment; } else if (!preferenceFragment.equals(defaultFragment) && !VALID_PREFERENCE_FRAGMENTS.contains(preferenceFragment)) { throw new IllegalArgumentException( "Invalid fragment for this activity: " + preferenceFragment); } else { return preferenceFragment; } } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { } 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, fragment) .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(PreferenceFragmentCompat.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 { private String mHighLightKey; private boolean mPreferenceHighlighted = false; private Preference mDeveloperOptionPref; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { final Bundle args = getArguments(); mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_ARG_KEY); if (rootKey == null && !TextUtils.isEmpty(mHighLightKey)) { rootKey = getParentKeyForPref(mHighLightKey); } 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(); }); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted); } protected String getParentKeyForPref(String key) { return null; } /** * 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: DeviceProfile deviceProfile = InvariantDeviceProfile.INSTANCE.get( getContext()).getDeviceProfile(getContext()); if (deviceProfile.isTablet) { // Launcher supports rotation by default. No need to show this setting. return false; } // Initialize the UI once preference.setDefaultValue( RotationHelper.getAllowRotationDefaultValue(deviceProfile)); return true; case FLAGS_PREFERENCE_KEY: // Only show flag toggler UI if this build variant implements that. return FeatureFlags.showFlagTogglerUi(getContext()); case DEVELOPER_OPTIONS_KEY: mDeveloperOptionPref = preference; return updateDeveloperOption(); } return true; } /** * Show if plugins are enabled or flag UI is enabled. * @return True if we should show the preference option. */ private boolean updateDeveloperOption() { boolean showPreference = FeatureFlags.showFlagTogglerUi(getContext()) || PluginManagerWrapper.hasPlugins(getContext()); if (mDeveloperOptionPref != null) { mDeveloperOptionPref.setEnabled(showPreference); if (showPreference) { getPreferenceScreen().addPreference(mDeveloperOptionPref); } else { getPreferenceScreen().removePreference(mDeveloperOptionPref); } } return showPreference; } @Override public void onResume() { super.onResume(); updateDeveloperOption(); if (isAdded() && !mPreferenceHighlighted) { PreferenceHighlighter highlighter = createHighlighter(); if (highlighter != null) { getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS); mPreferenceHighlighted = true; } else { requestAccessibilityFocus(getListView()); } } } 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; } private void requestAccessibilityFocus(@NonNull final RecyclerView rv) { rv.post(() -> { if (!rv.hasFocus() && rv.getChildCount() > 0) { rv.getChildAt(0) .performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null); } }); } } }