| /* |
| * 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<String> 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())) { |
| if (getPreferenceScreen().getTitle().equals( |
| getResources().getString(R.string.search_pref_screen_title))){ |
| DeviceProfile mDeviceProfile = InvariantDeviceProfile.INSTANCE.get( |
| getContext()).getDeviceProfile(getContext()); |
| getPreferenceScreen().setTitle(mDeviceProfile.isTablet ? |
| R.string.search_pref_screen_title_tablet |
| : R.string.search_pref_screen_title); |
| } |
| 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); |
| } |
| }); |
| } |
| } |
| } |