Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6eb256d7 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Simplifying settings activity

> Removing multiple fragments
> Exposing support to select preference root
> Adding support for recreating preferences
> Moving Developer options to same fragment

Bug: 305084752
Flag: N/A
Test: Manual
Change-Id: I499be1938ef8ed58cbc7f9b0f4ad3510d4b306c8
parent 9070b93f
Loading
Loading
Loading
Loading
+49 −133
Original line number Diff line number Diff line
@@ -15,9 +15,6 @@
 */
package com.android.launcher3.uioverrides.flags;

import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.view.View.GONE;
@@ -30,7 +27,7 @@ import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_H
import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT;
import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE;
import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_TIMEOUT_MS;
import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_HIGHLIGHT_KEY;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
@@ -40,7 +37,6 @@ import static com.android.launcher3.util.OnboardingPrefs.HOTSEAT_DISCOVERY_TIP_C
import static com.android.launcher3.util.OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN;
import static com.android.launcher3.util.OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP;

import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -48,23 +44,17 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceDataStore;
@@ -81,7 +71,6 @@ import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.SimpleBroadcastReceiver;

import java.util.ArrayList;
import java.util.List;
@@ -92,30 +81,32 @@ import java.util.stream.Collectors;
 * Dev-build only UI allowing developers to toggle flag settings and plugins.
 * See {@link FeatureFlags}.
 */
@TargetApi(Build.VERSION_CODES.O)
public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
public class DeveloperOptionsUI {

    private static final String ACTION_PLUGIN_SETTINGS = "com.android.systemui.action.PLUGIN_SETTINGS";
    private static final String ACTION_PLUGIN_SETTINGS =
            "com.android.systemui.action.PLUGIN_SETTINGS";
    private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";

    private final SimpleBroadcastReceiver mPluginReceiver =
            new SimpleBroadcastReceiver(i -> loadPluginPrefs());

    private PreferenceScreen mPreferenceScreen;
    private final PreferenceFragmentCompat mFragment;
    private final PreferenceScreen mPreferenceScreen;

    private PreferenceCategory mPluginsCategory;
    private FlagTogglerPrefUi mFlagTogglerPrefUi;

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        mPluginReceiver.registerPkgActions(getContext(), null,
                ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REMOVED);
        mPluginReceiver.register(getContext(), Intent.ACTION_USER_UNLOCKED);
    public DeveloperOptionsUI(PreferenceFragmentCompat fragment, PreferenceCategory flags) {
        mFragment = fragment;
        mPreferenceScreen = fragment.getPreferenceScreen();

        mPreferenceScreen = getPreferenceManager().createPreferenceScreen(getContext());
        setPreferenceScreen(mPreferenceScreen);
        // Add search bar
        View listView = mFragment.getListView();
        ViewGroup parent = (ViewGroup) listView.getParent();
        View topBar = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.developer_options_top_bar, parent, false);
        parent.addView(topBar, parent.indexOfChild(listView));
        initSearch(topBar.findViewById(R.id.filter_box));

        new FlagTogglerPrefUi(mFragment.requireActivity(), topBar.findViewById(R.id.flag_apply_btn))
                .applyTo(flags);

        initFlags();
        loadPluginPrefs();
        maybeAddSandboxCategory();
        addOnboardingPrefsCatergory();
@@ -123,10 +114,6 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
            addAllAppsFromOverviewCatergory();
        }
        addCustomLpnhCategory();

        if (getActivity() != null) {
            getActivity().setTitle("Developer Options");
        }
    }

    private void filterPreferences(String query, PreferenceGroup pg) {
@@ -149,21 +136,13 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
        pg.setVisible(hidden != count);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        EditText filterBox = view.findViewById(R.id.filter_box);
        filterBox.setVisibility(VISIBLE);
    private void initSearch(EditText filterBox) {
        filterBox.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { }

            @Override
            public void afterTextChanged(Editable editable) {
@@ -172,85 +151,33 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
            }
        });

        if (getArguments() != null) {
            String filter = getArguments().getString(EXTRA_FRAGMENT_ARG_KEY);
        if (mFragment.getArguments() != null) {
            String filter = mFragment.getArguments().getString(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
            // Normally EXTRA_FRAGMENT_ARG_KEY is used to highlight the preference with the given
            // key. This is a slight variation where we instead filter by the human-readable titles.
            if (filter != null) {
                filterBox.setText(filter);
            }
        }

        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 onDestroy() {
        super.onDestroy();
        mPluginReceiver.unregisterReceiverSafely(getContext());
    }

    private PreferenceCategory newCategory(String title) {
        return newCategory(title, null);
    }

    private PreferenceCategory newCategory(String title, @Nullable String summary) {
        PreferenceCategory category = new PreferenceCategory(getContext());
        category.setOrder(Preference.DEFAULT_ORDER);
        category.setTitle(title);
        if (!TextUtils.isEmpty(summary)) {
            category.setSummary(summary);
        }
        mPreferenceScreen.addPreference(category);
        return category;
    }

    private void initFlags() {
        if (!FeatureFlags.showFlagTogglerUi(getContext())) {
            return;
        }

        mFlagTogglerPrefUi = new FlagTogglerPrefUi(this);
        mFlagTogglerPrefUi.applyTo(newCategory("Feature flags", "Long press to reset"));
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        if (mFlagTogglerPrefUi != null) {
            mFlagTogglerPrefUi.onCreateOptionsMenu(menu);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mFlagTogglerPrefUi != null) {
            mFlagTogglerPrefUi.onOptionsItemSelected(item);
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onStop() {
        if (mFlagTogglerPrefUi != null) {
            mFlagTogglerPrefUi.onStop();
        }
        super.onStop();
    private Context getContext() {
        return mFragment.requireContext();
    }

    private void loadPluginPrefs() {
        if (mPluginsCategory != null) {
            mPreferenceScreen.removePreference(mPluginsCategory);
        }
        if (!PluginManagerWrapper.hasPlugins(getActivity())) {
        if (!PluginManagerWrapper.hasPlugins(getContext())) {
            mPluginsCategory = null;
            return;
        }
@@ -323,68 +250,57 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
        launchTutorialStepMenuPreference.setKey("launchTutorialStepMenu");
        launchTutorialStepMenuPreference.setTitle("Launch Gesture Tutorial Steps menu");
        launchTutorialStepMenuPreference.setSummary("Select a gesture tutorial step.");
        launchTutorialStepMenuPreference.setOnPreferenceClickListener(preference -> {
            startActivity(launchSandboxIntent.putExtra("use_tutorial_menu", true));
            return true;
        });
        launchTutorialStepMenuPreference.setIntent(
                new Intent(launchSandboxIntent).putExtra("use_tutorial_menu", true));

        sandboxCategory.addPreference(launchTutorialStepMenuPreference);
        Preference launchOnboardingTutorialPreference = new Preference(context);
        launchOnboardingTutorialPreference.setKey("launchOnboardingTutorial");
        launchOnboardingTutorialPreference.setTitle("Launch Onboarding Tutorial");
        launchOnboardingTutorialPreference.setSummary("Learn the basic navigation gestures.");
        launchOnboardingTutorialPreference.setOnPreferenceClickListener(preference -> {
            startActivity(launchSandboxIntent
        launchTutorialStepMenuPreference.setIntent(new Intent(launchSandboxIntent)
                .putExtra("use_tutorial_menu", false)
                .putExtra("tutorial_steps",
                        new String[] {
                                "HOME_NAVIGATION",
                                "BACK_NAVIGATION",
                                "OVERVIEW_NAVIGATION"}));
            return true;
        });

        sandboxCategory.addPreference(launchOnboardingTutorialPreference);
        Preference launchBackTutorialPreference = new Preference(context);
        launchBackTutorialPreference.setKey("launchBackTutorial");
        launchBackTutorialPreference.setTitle("Launch Back Tutorial");
        launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
        launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
            startActivity(launchSandboxIntent
        launchBackTutorialPreference.setIntent(new Intent(launchSandboxIntent)
                    .putExtra("use_tutorial_menu", false)
                    .putExtra("tutorial_steps", new String[] {"BACK_NAVIGATION"}));
            return true;
        });

        sandboxCategory.addPreference(launchBackTutorialPreference);
        Preference launchHomeTutorialPreference = new Preference(context);
        launchHomeTutorialPreference.setKey("launchHomeTutorial");
        launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
        launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
        launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
            startActivity(launchSandboxIntent
        launchHomeTutorialPreference.setIntent(new Intent(launchSandboxIntent)
                    .putExtra("use_tutorial_menu", false)
                    .putExtra("tutorial_steps", new String[] {"HOME_NAVIGATION"}));
            return true;
        });

        sandboxCategory.addPreference(launchHomeTutorialPreference);
        Preference launchOverviewTutorialPreference = new Preference(context);
        launchOverviewTutorialPreference.setKey("launchOverviewTutorial");
        launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
        launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
        launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
            startActivity(launchSandboxIntent
        launchOverviewTutorialPreference.setIntent(new Intent(launchSandboxIntent)
                    .putExtra("use_tutorial_menu", false)
                    .putExtra("tutorial_steps", new String[] {"OVERVIEW_NAVIGATION"}));
            return true;
        });

        sandboxCategory.addPreference(launchOverviewTutorialPreference);
        Preference launchSecondaryDisplayPreference = new Preference(context);
        launchSecondaryDisplayPreference.setKey("launchSecondaryDisplay");
        launchSecondaryDisplayPreference.setTitle("Launch Secondary Display");
        launchSecondaryDisplayPreference.setSummary("Launch secondary display activity");
        launchSecondaryDisplayPreference.setOnPreferenceClickListener(preference -> {
            startActivity(new Intent(context, SecondaryDisplayLauncher.class));
            return true;
        });
        sandboxCategory.addPreference(launchSecondaryDisplayPreference);
        launchSecondaryDisplayPreference.setIntent(
                new Intent(context, SecondaryDisplayLauncher.class));

    }

    private void addOnboardingPrefsCatergory() {
+20 −28
Original line number Diff line number Diff line
@@ -19,24 +19,23 @@ package com.android.launcher3.uioverrides.flags;
import static com.android.launcher3.config.FeatureFlags.FlagState.TEAMFOOD;
import static com.android.launcher3.uioverrides.flags.FlagsFactory.TEAMFOOD_FLAG;

import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Process;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

import androidx.preference.PreferenceDataStore;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;

import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;

import java.util.List;
import java.util.Set;
@@ -44,12 +43,13 @@ import java.util.Set;
/**
 * Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
 */
public final class FlagTogglerPrefUi {
public final class FlagTogglerPrefUi implements ActivityLifecycleCallbacksAdapter {

    private static final String TAG = "FlagTogglerPrefFrag";

    private final PreferenceFragmentCompat mFragment;
    private final View mFlagsApplyButton;
    private final Context mContext;

    private final PreferenceDataStore mDataStore = new PreferenceDataStore() {

        @Override
@@ -64,9 +64,17 @@ public final class FlagTogglerPrefUi {
        }
    };

    public FlagTogglerPrefUi(PreferenceFragmentCompat fragment) {
        mFragment = fragment;
        mContext = fragment.getActivity();
    public FlagTogglerPrefUi(Activity activity, View flagsApplyButton) {
        mFlagsApplyButton = flagsApplyButton;
        mContext = mFlagsApplyButton.getContext();
        activity.registerActivityLifecycleCallbacks(this);

        mFlagsApplyButton.setOnClickListener(v -> {
            FlagsFactory.getSharedPreferences().edit().commit();
            Log.e(TAG,
                    "Killing launcher process " + Process.myPid() + " to apply new flag values");
            System.exit(0);
        });
    }

    public void applyTo(PreferenceGroup parent) {
@@ -137,27 +145,11 @@ public final class FlagTogglerPrefUi {
    }

    private void updateMenu() {
        mFragment.setHasOptionsMenu(anyChanged());
        mFragment.getActivity().invalidateOptionsMenu();
        mFlagsApplyButton.setVisibility(anyChanged() ? View.VISIBLE : View.INVISIBLE);
    }

    public void onCreateOptionsMenu(Menu menu) {
        if (anyChanged()) {
            menu.add(0, R.id.menu_apply_flags, 0, "Apply")
                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
        }
    }

    public void onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.menu_apply_flags) {
            FlagsFactory.getSharedPreferences().edit().commit();
            Log.e(TAG,
                    "Killing launcher process " + Process.myPid() + " to apply new flag values");
            System.exit(0);
        }
    }

    public void onStop() {
    @Override
    public void onActivityStopped(Activity activity) {
        if (anyChanged()) {
            Toast.makeText(mContext, "Flag won't be applied until you restart launcher",
                    Toast.LENGTH_LONG).show();
+26 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:showDividers="middle">

    <EditText
        android:id="@+id/filter_box"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="@dimen/developer_options_filter_margins"
        android:hint="@string/developer_options_filter_hint"
        android:inputType="text"
        android:maxLines="1"
        android:imeOptions="actionDone"
        />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:text="Apply"
        android:visibility="invisible"
        android:id="@+id/flag_apply_btn" />
</LinearLayout>
 No newline at end of file
+2 −19
Original line number Diff line number Diff line
@@ -2,23 +2,6 @@
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/filter_box"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="@dimen/developer_options_filter_margins"
        android:hint="@string/developer_options_filter_hint"
        android:visibility="gone"
        android:inputType="text"
        android:maxLines="1"
        android:imeOptions="actionDone"
        />

    <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@android:id/list_container"/>
</LinearLayout>
 No newline at end of file
+0 −6
Original line number Diff line number Diff line
@@ -58,7 +58,6 @@ import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.os.TransactionTooLargeException;
import android.provider.Settings;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
@@ -168,11 +167,6 @@ public final class Utilities {
        return nightMode == Configuration.UI_MODE_NIGHT_YES;
    }

    public static boolean isDevelopersOptionsEnabled(Context context) {
        return Settings.Global.getInt(context.getApplicationContext().getContentResolver(),
                        Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
    }

    private static boolean sIsRunningInTestHarness = ActivityManager.isRunningInTestHarness();

    public static boolean isRunningInTestHarness() {
Loading