Using support lib implementation for launcher preference

Bug: 117519297
Change-Id: Icea5e022a337436e48db9376fd441f805dc34e54
diff --git a/Android.mk b/Android.mk
index b8e6c85..bf9b8a0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -67,7 +67,8 @@
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     androidx.recyclerview_recyclerview \
-    androidx.dynamicanimation_dynamicanimation
+    androidx.dynamicanimation_dynamicanimation \
+    androidx.preference_preference
 
 LOCAL_STATIC_JAVA_LIBRARIES := libPluginCore
 
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 8f4d5be..5b00b7d 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -156,7 +156,7 @@
         The settings activity. To extend point settings_fragment_name to appropriate fragment class
         -->
         <activity
-            android:name="com.android.launcher3.SettingsActivity"
+            android:name="com.android.launcher3.settings.SettingsActivity"
             android:label="@string/settings_button_text"
             android:theme="@android:style/Theme.DeviceDefault.Settings"
             android:autoRemoveFromRecents="true">
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 1df7c2f..c55cc49 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -14,9 +14,10 @@
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<androidx.preference.PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <com.android.launcher3.views.ButtonPreference
+    <com.android.launcher3.settings.IconBadgingPreference
         android:key="pref_icon_badging"
         android:title="@string/icon_badging_title"
         android:persistent="false"
@@ -27,7 +28,7 @@
                 android:name=":settings:fragment_args_key"
                 android:value="notification_badging" />
         </intent>
-    </com.android.launcher3.views.ButtonPreference>
+    </com.android.launcher3.settings.IconBadgingPreference>
 
     <SwitchPreference
         android:key="pref_add_icon_to_home"
@@ -52,10 +53,10 @@
         android:defaultValue=""
         android:persistent="false" />
 
-    <PreferenceScreen
+    <androidx.preference.PreferenceScreen
         android:fragment="com.android.launcher3.config.FlagTogglerPreferenceFragment"
         android:key="flag_toggler"
         android:persistent="false"
         android:title="Feature flags"/>
 
-</PreferenceScreen>
+</androidx.preference.PreferenceScreen>
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
deleted file mode 100644
index a17f614..0000000
--- a/src/com/android/launcher3/SettingsActivity.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * 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;
-
-import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
-import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
-import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.Adapter;
-import android.widget.ListView;
-
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.IconShapeOverride;
-import com.android.launcher3.notification.NotificationListener;
-import com.android.launcher3.util.ListViewHighlighter;
-import com.android.launcher3.util.SecureSettingsObserver;
-import com.android.launcher3.views.ButtonPreference;
-
-import java.util.Objects;
-
-/**
- * Settings activity for Launcher. Currently implements the following setting: Allow rotation
- */
-public class SettingsActivity extends Activity
-        implements PreferenceFragment.OnPreferenceStartFragmentCallback {
-
-    private static final String FLAGS_PREFERENCE_KEY = "flag_toggler";
-
-    private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
-    /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
-    private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
-
-    private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
-    private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
-    private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
-    private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (savedInstanceState == null) {
-            Fragment f = Fragment.instantiate(this, getString(R.string.settings_fragment_name));
-            // Display the fragment as the main content.
-            getFragmentManager().beginTransaction()
-                    .replace(android.R.id.content, f)
-                    .commit();
-        }
-    }
-
-    protected PreferenceFragment getNewFragment() {
-        return new LauncherSettingsFragment();
-    }
-
-    @Override
-    public boolean onPreferenceStartFragment(
-            PreferenceFragment preferenceFragment, Preference pref) {
-        if (getFragmentManager().isStateSaved()) {
-            // Sometimes onClick can come after onPause because of being posted on the handler.
-            // Skip starting new fragments in that case.
-            return false;
-        }
-        Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras());
-        if (f instanceof DialogFragment) {
-            ((DialogFragment) f).show(getFragmentManager(), pref.getKey());
-        } else {
-            getFragmentManager()
-                    .beginTransaction()
-                    .replace(android.R.id.content, f)
-                    .addToBackStack(pref.getKey())
-                    .commit();
-        }
-        return true;
-    }
-
-    /**
-     * This fragment shows the launcher preferences.
-     */
-    public static class LauncherSettingsFragment extends PreferenceFragment {
-
-        private SecureSettingsObserver mIconBadgingObserver;
-
-        private String mPreferenceKey;
-        private boolean mPreferenceHighlighted = false;
-
-        @Override
-        public void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-            if (savedInstanceState != null) {
-                mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
-            }
-
-            getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
-            addPreferencesFromResource(R.xml.launcher_preferences);
-
-            // Only show flag toggler UI if this build variant implements that.
-            Preference flagToggler = findPreference(FLAGS_PREFERENCE_KEY);
-            if (flagToggler != null && !FeatureFlags.showFlagTogglerUi()) {
-                getPreferenceScreen().removePreference(flagToggler);
-            }
-
-            ContentResolver resolver = getActivity().getContentResolver();
-
-            ButtonPreference iconBadgingPref =
-                    (ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY);
-            if (!Utilities.ATLEAST_OREO) {
-                getPreferenceScreen().removePreference(
-                        findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
-                getPreferenceScreen().removePreference(iconBadgingPref);
-            } else if (!getResources().getBoolean(R.bool.notification_badging_enabled)) {
-                getPreferenceScreen().removePreference(iconBadgingPref);
-            } else {
-                // Listen to system notification badge settings while this UI is active.
-                mIconBadgingObserver = newNotificationSettingsObserver(
-                        getActivity(), new IconBadgingObserver(iconBadgingPref, resolver));
-                mIconBadgingObserver.register();
-                // Also listen if notification permission changes
-                mIconBadgingObserver.getResolver().registerContentObserver(
-                        Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS), false,
-                        mIconBadgingObserver);
-                mIconBadgingObserver.dispatchOnChange();
-            }
-
-            Preference iconShapeOverride = findPreference(IconShapeOverride.KEY_PREFERENCE);
-            if (iconShapeOverride != null) {
-                if (IconShapeOverride.isSupported(getActivity())) {
-                    IconShapeOverride.handlePreferenceUi((ListPreference) iconShapeOverride);
-                } else {
-                    getPreferenceScreen().removePreference(iconShapeOverride);
-                }
-            }
-
-            // Setup allow rotation preference
-            Preference rotationPref = findPreference(ALLOW_ROTATION_PREFERENCE_KEY);
-            if (getResources().getBoolean(R.bool.allow_rotation)) {
-                // Launcher supports rotation by default. No need to show this setting.
-                getPreferenceScreen().removePreference(rotationPref);
-            } else {
-                // Initialize the UI once
-                rotationPref.setDefaultValue(getAllowRotationDefaultValue());
-            }
-        }
-
-        @Override
-        public void onSaveInstanceState(Bundle outState) {
-            super.onSaveInstanceState(outState);
-            outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
-        }
-
-        @Override
-        public void onResume() {
-            super.onResume();
-
-            Intent intent = getActivity().getIntent();
-            mPreferenceKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
-            if (isAdded() && !mPreferenceHighlighted && !TextUtils.isEmpty(mPreferenceKey)) {
-                getView().postDelayed(this::highlightPreference, DELAY_HIGHLIGHT_DURATION_MILLIS);
-            }
-        }
-
-        private void highlightPreference() {
-            Preference pref = findPreference(mPreferenceKey);
-            if (pref == null || getPreferenceScreen() == null) {
-                return;
-            }
-            PreferenceScreen screen = getPreferenceScreen();
-            if (Utilities.ATLEAST_OREO) {
-                screen = selectPreferenceRecursive(pref, screen);
-            }
-            if (screen == null) {
-                return;
-            }
-
-            View root = screen.getDialog() != null
-                    ? screen.getDialog().getWindow().getDecorView() : getView();
-            ListView list = root.findViewById(android.R.id.list);
-            if (list == null || list.getAdapter() == null) {
-                return;
-            }
-            Adapter adapter = list.getAdapter();
-
-            // Find the position
-            int position = -1;
-            for (int i = adapter.getCount() - 1; i >= 0; i--) {
-                if (pref == adapter.getItem(i)) {
-                    position = i;
-                    break;
-                }
-            }
-            new ListViewHighlighter(list, position);
-            mPreferenceHighlighted = true;
-        }
-
-        @Override
-        public void onDestroy() {
-            if (mIconBadgingObserver != null) {
-                mIconBadgingObserver.unregister();
-                mIconBadgingObserver = null;
-            }
-            super.onDestroy();
-        }
-
-        @TargetApi(Build.VERSION_CODES.O)
-        private PreferenceScreen selectPreferenceRecursive(
-                Preference pref, PreferenceScreen topParent) {
-            if (!(pref.getParent() instanceof PreferenceScreen)) {
-                return null;
-            }
-
-            PreferenceScreen parent = (PreferenceScreen) pref.getParent();
-            if (Objects.equals(parent.getKey(), topParent.getKey())) {
-                return parent;
-            } else if (selectPreferenceRecursive(parent, topParent) != null) {
-                ((PreferenceScreen) parent.getParent())
-                        .onItemClick(null, null, parent.getOrder(), 0);
-                return parent;
-            } else {
-                return null;
-            }
-        }
-    }
-
-    /**
-     * Content observer which listens for system badging setting changes,
-     * and updates the launcher badging setting subtext accordingly.
-     */
-    private static class IconBadgingObserver implements SecureSettingsObserver.OnChangeListener {
-
-        private final ButtonPreference mBadgingPref;
-        private final ContentResolver mResolver;
-
-        public IconBadgingObserver(ButtonPreference badgingPref, ContentResolver resolver) {
-            mBadgingPref = badgingPref;
-            mResolver = resolver;
-        }
-
-        @Override
-        public void onSettingsChanged(boolean enabled) {
-            int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off;
-
-            boolean serviceEnabled = true;
-            if (enabled) {
-                // Check if the listener is enabled or not.
-                String enabledListeners =
-                        Settings.Secure.getString(mResolver, NOTIFICATION_ENABLED_LISTENERS);
-                ComponentName myListener =
-                        new ComponentName(mBadgingPref.getContext(), NotificationListener.class);
-                serviceEnabled = enabledListeners != null &&
-                        (enabledListeners.contains(myListener.flattenToString()) ||
-                                enabledListeners.contains(myListener.flattenToShortString()));
-                if (!serviceEnabled) {
-                    summary = R.string.title_missing_notification_access;
-                }
-            }
-            mBadgingPref.setWidgetFrameVisible(!serviceEnabled);
-            mBadgingPref.setFragment(
-                    serviceEnabled ? null : NotificationAccessConfirmation.class.getName());
-            mBadgingPref.setSummary(summary);
-
-        }
-    }
-
-    public static class NotificationAccessConfirmation
-            extends DialogFragment implements DialogInterface.OnClickListener {
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-            String msg = context.getString(R.string.msg_missing_notification_access,
-                    context.getString(R.string.derived_app_name));
-            return new AlertDialog.Builder(context)
-                    .setTitle(R.string.title_missing_notification_access)
-                    .setMessage(msg)
-                    .setNegativeButton(android.R.string.cancel, null)
-                    .setPositiveButton(R.string.title_change_settings, this)
-                    .create();
-        }
-
-        @Override
-        public void onClick(DialogInterface dialogInterface, int i) {
-            ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
-            Bundle showFragmentArgs = new Bundle();
-            showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString());
-
-            Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
-                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                    .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString())
-                    .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
-            getActivity().startActivity(intent);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/graphics/IconShapeOverride.java b/src/com/android/launcher3/graphics/IconShapeOverride.java
index cadc6e3..b636c6d 100644
--- a/src/com/android/launcher3/graphics/IconShapeOverride.java
+++ b/src/com/android/launcher3/graphics/IconShapeOverride.java
@@ -26,9 +26,6 @@
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.SystemClock;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -42,6 +39,9 @@
 import java.lang.reflect.Field;
 
 import androidx.annotation.NonNull;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
 
 /**
  * Utility class to override shape of {@link android.graphics.drawable.AdaptiveIconDrawable}.
diff --git a/src/com/android/launcher3/settings/IconBadgingPreference.java b/src/com/android/launcher3/settings/IconBadgingPreference.java
new file mode 100644
index 0000000..7c97b38
--- /dev/null
+++ b/src/com/android/launcher3/settings/IconBadgingPreference.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 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 com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGS;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.util.SecureSettingsObserver;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * A {@link Preference} for indicating icon badging status.
+ * Also has utility methods for updating UI based on badging status changes.
+ */
+public class IconBadgingPreference extends Preference
+        implements SecureSettingsObserver.OnChangeListener {
+
+    private boolean mWidgetFrameVisible = false;
+
+    /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
+    private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
+
+    public IconBadgingPreference(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public IconBadgingPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public IconBadgingPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public IconBadgingPreference(Context context) {
+        super(context);
+    }
+
+    private void setWidgetFrameVisible(boolean isVisible) {
+        if (mWidgetFrameVisible != isVisible) {
+            mWidgetFrameVisible = isVisible;
+            notifyChanged();
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        View widgetFrame = holder.findViewById(android.R.id.widget_frame);
+        if (widgetFrame != null) {
+            widgetFrame.setVisibility(mWidgetFrameVisible ? View.VISIBLE : View.GONE);
+        }
+    }
+
+    @Override
+    public void onSettingsChanged(boolean enabled) {
+        int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off;
+
+        boolean serviceEnabled = true;
+        if (enabled) {
+            // Check if the listener is enabled or not.
+            String enabledListeners = Settings.Secure.getString(
+                    getContext().getContentResolver(), NOTIFICATION_ENABLED_LISTENERS);
+            ComponentName myListener =
+                    new ComponentName(getContext(), NotificationListener.class);
+            serviceEnabled = enabledListeners != null &&
+                    (enabledListeners.contains(myListener.flattenToString()) ||
+                            enabledListeners.contains(myListener.flattenToShortString()));
+            if (!serviceEnabled) {
+                summary = R.string.title_missing_notification_access;
+            }
+        }
+        setWidgetFrameVisible(!serviceEnabled);
+        setFragment(serviceEnabled ? null : NotificationAccessConfirmation.class.getName());
+        setSummary(summary);
+    }
+
+    public static class NotificationAccessConfirmation
+            extends DialogFragment implements DialogInterface.OnClickListener {
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+            String msg = context.getString(R.string.msg_missing_notification_access,
+                    context.getString(R.string.derived_app_name));
+            return new AlertDialog.Builder(context)
+                    .setTitle(R.string.title_missing_notification_access)
+                    .setMessage(msg)
+                    .setNegativeButton(android.R.string.cancel, null)
+                    .setPositiveButton(R.string.title_change_settings, this)
+                    .create();
+        }
+
+        @Override
+        public void onClick(DialogInterface dialogInterface, int i) {
+            ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
+            Bundle showFragmentArgs = new Bundle();
+            showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString());
+
+            Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString())
+                    .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
+            getActivity().startActivity(intent);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/settings/PreferenceHighlighter.java b/src/com/android/launcher3/settings/PreferenceHighlighter.java
new file mode 100644
index 0000000..4ed4cf1
--- /dev/null
+++ b/src/com/android/launcher3/settings/PreferenceHighlighter.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 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.graphics.ColorUtils.setAlphaComponent;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.Property;
+import android.view.View;
+
+import com.android.launcher3.util.Themes;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
+import androidx.recyclerview.widget.RecyclerView.State;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Utility class for highlighting a preference
+ */
+public class PreferenceHighlighter extends ItemDecoration implements Runnable {
+
+    private static final Property<PreferenceHighlighter, Integer> HIGHLIGHT_COLOR =
+            new Property<PreferenceHighlighter, Integer>(Integer.TYPE, "highlightColor") {
+
+                @Override
+                public Integer get(PreferenceHighlighter highlighter) {
+                    return highlighter.mHighlightColor;
+                }
+
+                @Override
+                public void set(PreferenceHighlighter highlighter, Integer value) {
+                    highlighter.mHighlightColor = value;
+                    highlighter.mRv.invalidateItemDecorations();
+                }
+            };
+
+    private static final long HIGHLIGHT_DURATION = 15000L;
+    private static final long HIGHLIGHT_FADE_OUT_DURATION = 500L;
+    private static final long HIGHLIGHT_FADE_IN_DURATION = 200L;
+    private static final int END_COLOR = setAlphaComponent(Color.WHITE, 0);
+
+    private final Paint mPaint = new Paint();
+    private final RecyclerView mRv;
+    private final int mIndex;
+
+    private boolean mHighLightStarted = false;
+    private int mHighlightColor = END_COLOR;
+
+
+    public PreferenceHighlighter(RecyclerView rv, int index) {
+        mRv = rv;
+        mIndex = index;
+    }
+
+    @Override
+    public void run() {
+        mRv.addItemDecoration(this);
+        mRv.smoothScrollToPosition(mIndex);
+    }
+
+    @Override
+    public void onDraw(Canvas c, RecyclerView parent, State state) {
+        ViewHolder holder = parent.findViewHolderForAdapterPosition(mIndex);
+        if (holder == null) {
+            return;
+        }
+        if (!mHighLightStarted && state.getRemainingScrollVertical() != 0) {
+            // Wait until scrolling stopped
+            return;
+        }
+
+        if (!mHighLightStarted) {
+            // Start highlight
+            int colorTo = setAlphaComponent(Themes.getColorAccent(mRv.getContext()), 66);
+            ObjectAnimator anim = ObjectAnimator.ofArgb(this, HIGHLIGHT_COLOR, END_COLOR, colorTo);
+            anim.setDuration(HIGHLIGHT_FADE_IN_DURATION);
+            anim.setRepeatMode(ValueAnimator.REVERSE);
+            anim.setRepeatCount(4);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    removeHighlight();
+                }
+            });
+            anim.start();
+            mHighLightStarted = true;
+        }
+
+        View view = holder.itemView;
+        mPaint.setColor(mHighlightColor);
+        c.drawRect(0, view.getY(), parent.getWidth(), view.getY() + view.getHeight(), mPaint);
+    }
+
+    private void removeHighlight() {
+        ObjectAnimator anim = ObjectAnimator.ofArgb(
+                this, HIGHLIGHT_COLOR, mHighlightColor, END_COLOR);
+        anim.setDuration(HIGHLIGHT_FADE_OUT_DURATION);
+        anim.setStartDelay(HIGHLIGHT_DURATION);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mRv.removeItemDecoration(PreferenceHighlighter.this);
+            }
+        });
+        anim.start();
+    }
+}
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
new file mode 100644
index 0000000..66420d0
--- /dev/null
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -0,0 +1,250 @@
+/*
+ * 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 com.android.launcher3.SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY;
+import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
+import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
+import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+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.graphics.IconShapeOverride;
+import com.android.launcher3.util.SecureSettingsObserver;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceFragment.OnPreferenceStartFragmentCallback;
+import androidx.preference.PreferenceFragment.OnPreferenceStartScreenCallback;
+import androidx.preference.PreferenceGroup.PreferencePositionCallback;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Settings activity for Launcher. Currently implements the following setting: Allow rotation
+ */
+public class SettingsActivity extends Activity
+        implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback {
+
+    private static final String FLAGS_PREFERENCE_KEY = "flag_toggler";
+
+    private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
+    /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
+    private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
+
+    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";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState == null) {
+            Bundle args = new Bundle();
+            String prefKey = getIntent().getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
+            if (!TextUtils.isEmpty(prefKey)) {
+                args.putString(EXTRA_FRAGMENT_ARG_KEY, prefKey);
+            }
+
+            Fragment f = Fragment.instantiate(
+                    this, getString(R.string.settings_fragment_name), args);
+            // Display the fragment as the main content.
+            getFragmentManager().beginTransaction()
+                    .replace(android.R.id.content, f)
+                    .commit();
+        }
+    }
+
+    private boolean startFragment(String fragment, Bundle args, String key) {
+        if (Utilities.ATLEAST_P && getFragmentManager().isStateSaved()) {
+            // Sometimes onClick can come after onPause because of being posted on the handler.
+            // Skip starting new fragments in that case.
+            return false;
+        }
+        Fragment f = Fragment.instantiate(this, fragment, args);
+        if (f instanceof DialogFragment) {
+            ((DialogFragment) f).show(getFragmentManager(), key);
+        } else {
+            getFragmentManager()
+                    .beginTransaction()
+                    .replace(android.R.id.content, f)
+                    .addToBackStack(key)
+                    .commit();
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onPreferenceStartFragment(
+            PreferenceFragment preferenceFragment, Preference pref) {
+        return startFragment(pref.getFragment(), pref.getExtras(), pref.getKey());
+    }
+
+    @Override
+    public boolean onPreferenceStartScreen(PreferenceFragment caller, PreferenceScreen pref) {
+        Bundle args = new Bundle();
+        args.putString(PreferenceFragment.ARG_PREFERENCE_ROOT, pref.getKey());
+        return startFragment(getString(R.string.settings_fragment_name), args, pref.getKey());
+    }
+
+    /**
+     * This fragment shows the launcher preferences.
+     */
+    public static class LauncherSettingsFragment extends PreferenceFragment {
+
+        private SecureSettingsObserver mIconBadgingObserver;
+
+        private String mHighLightKey;
+        private boolean mPreferenceHighlighted = false;
+
+        @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);
+                }
+            }
+        }
+
+        @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 ICON_BADGING_PREFERENCE_KEY:
+                    if (!Utilities.ATLEAST_OREO ||
+                            !getResources().getBoolean(R.bool.notification_badging_enabled)) {
+                        return false;
+                    }
+
+                    // Listen to system notification badge settings while this UI is active.
+                    mIconBadgingObserver = newNotificationSettingsObserver(
+                            getActivity(), (IconBadgingPreference) preference);
+                    mIconBadgingObserver.register();
+                    // Also listen if notification permission changes
+                    mIconBadgingObserver.getResolver().registerContentObserver(
+                            Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS), false,
+                            mIconBadgingObserver);
+                    mIconBadgingObserver.dispatchOnChange();
+                    return true;
+
+                case ADD_ICON_PREFERENCE_KEY:
+                    return Utilities.ATLEAST_OREO;
+
+                case IconShapeOverride.KEY_PREFERENCE:
+                    if (!IconShapeOverride.isSupported(getActivity())) {
+                        return false;
+                    }
+                    IconShapeOverride.handlePreferenceUi((ListPreference) preference);
+                    return true;
+
+                case ALLOW_ROTATION_PREFERENCE_KEY:
+                    if (getResources().getBoolean(R.bool.allow_rotation)) {
+                        // Launcher supports rotation by default. No need to show this setting.
+                        return false;
+                    }
+                    // Initialize the UI once
+                    preference.setDefaultValue(getAllowRotationDefaultValue());
+                    return true;
+
+                case FLAGS_PREFERENCE_KEY:
+                    // Only show flag toggler UI if this build variant implements that.
+                    return FeatureFlags.showFlagTogglerUi();
+            }
+
+            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;
+                }
+            }
+        }
+
+        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) : null;
+        }
+
+        @Override
+        public void onDestroy() {
+            if (mIconBadgingObserver != null) {
+                mIconBadgingObserver.unregister();
+                mIconBadgingObserver = null;
+            }
+            super.onDestroy();
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/ListViewHighlighter.java b/src/com/android/launcher3/util/ListViewHighlighter.java
deleted file mode 100644
index c9fe228..0000000
--- a/src/com/android/launcher3/util/ListViewHighlighter.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2018 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.util;
-
-import android.animation.ArgbEvaluator;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.widget.AbsListView;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.AbsListView.RecyclerListener;
-import android.widget.ListView;
-
-import com.android.launcher3.R;
-
-import androidx.core.graphics.ColorUtils;
-
-/**
- * Utility class to scroll and highlight a list view item
- */
-public class ListViewHighlighter implements OnScrollListener, RecyclerListener,
-        OnLayoutChangeListener {
-
-    private final ListView mListView;
-    private int mPosHighlight;
-
-    private boolean mColorAnimated = false;
-
-    public ListViewHighlighter(ListView listView, int posHighlight) {
-        mListView = listView;
-        mPosHighlight = posHighlight;
-        mListView.setOnScrollListener(this);
-        mListView.setRecyclerListener(this);
-        mListView.addOnLayoutChangeListener(this);
-
-        mListView.post(this::tryHighlight);
-    }
-
-    @Override
-    public void onLayoutChange(View v, int left, int top, int right, int bottom,
-            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        mListView.post(this::tryHighlight);
-    }
-
-    private void tryHighlight() {
-        if (mPosHighlight < 0 || mListView.getChildCount() == 0) {
-            return;
-        }
-        if (!highlightIfVisible(mListView.getFirstVisiblePosition(),
-                mListView.getLastVisiblePosition())) {
-            mListView.smoothScrollToPosition(mPosHighlight);
-        }
-    }
-
-    @Override
-    public void onScrollStateChanged(AbsListView absListView, int i) { }
-
-    @Override
-    public void onScroll(AbsListView view, int firstVisibleItem,
-            int visibleItemCount, int totalItemCount) {
-        highlightIfVisible(firstVisibleItem, firstVisibleItem + visibleItemCount - 1);
-    }
-
-    private boolean highlightIfVisible(int start, int end) {
-        if (mPosHighlight < 0 || mListView.getChildCount() == 0) {
-            return false;
-        }
-        if (start > mPosHighlight || mPosHighlight > end) {
-            return false;
-        }
-        highlightView(mListView.getChildAt(mPosHighlight - start));
-
-        // finish highlight
-        mListView.setOnScrollListener(null);
-        mListView.removeOnLayoutChangeListener(this);
-
-        mPosHighlight = -1;
-        return true;
-    }
-
-    @Override
-    public void onMovedToScrapHeap(View view) {
-        unhighlightView(view);
-    }
-
-    private void highlightView(View view) {
-        if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
-            // already highlighted
-        } else {
-            view.setTag(R.id.view_highlighted, true);
-            view.setTag(R.id.view_unhighlight_background, view.getBackground());
-            view.setBackground(getHighlightBackground());
-            view.postDelayed(() -> {
-                unhighlightView(view);
-            }, 15000L);
-        }
-    }
-
-    private void unhighlightView(View view) {
-        if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
-            Object background = view.getTag(R.id.view_unhighlight_background);
-            if (background instanceof Drawable) {
-                view.setBackground((Drawable) background);
-            }
-            view.setTag(R.id.view_unhighlight_background, null);
-            view.setTag(R.id.view_highlighted, false);
-        }
-    }
-
-    private ColorDrawable getHighlightBackground() {
-        int color = ColorUtils.setAlphaComponent(Themes.getColorAccent(mListView.getContext()), 26);
-        if (mColorAnimated) {
-            return new ColorDrawable(color);
-        }
-        mColorAnimated = true;
-        ColorDrawable bg = new ColorDrawable(Color.WHITE);
-        ObjectAnimator anim = ObjectAnimator.ofInt(bg, "color", Color.WHITE, color);
-        anim.setEvaluator(new ArgbEvaluator());
-        anim.setDuration(200L);
-        anim.setRepeatMode(ValueAnimator.REVERSE);
-        anim.setRepeatCount(4);
-        anim.start();
-        return bg;
-    }
-}
diff --git a/src/com/android/launcher3/views/ButtonPreference.java b/src/com/android/launcher3/views/ButtonPreference.java
deleted file mode 100644
index fdcf2ca..0000000
--- a/src/com/android/launcher3/views/ButtonPreference.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 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.views;
-
-import android.content.Context;
-import android.preference.Preference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Extension of {@link Preference} which makes the widget layout clickable.
- *
- * @see #setWidgetLayoutResource(int)
- */
-public class ButtonPreference extends Preference {
-
-    private boolean mWidgetFrameVisible = false;
-
-    public ButtonPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    public ButtonPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public ButtonPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public ButtonPreference(Context context) {
-        super(context);
-    }
-
-    public void setWidgetFrameVisible(boolean isVisible) {
-        if (mWidgetFrameVisible != isVisible) {
-            mWidgetFrameVisible = isVisible;
-            notifyChanged();
-        }
-    }
-
-    @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-
-        ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame);
-        if (widgetFrame != null) {
-            widgetFrame.setVisibility(mWidgetFrameVisible ? View.VISIBLE : View.GONE);
-        }
-    }
-}