OmniLib: Introduce OmniRom SDK

 * omnilib-res app will be installed to system/frameworks for the futur internal ressources add-on
 * OmniLib java_library will be installed to system/frameworks for the futur internal java/aidl add-on
 * The OmniPreference android_library need to be moved outside system/frameworks  because of some nested dependency breakage

Change-Id: I2b9a7bc7f4aa72052ae6600933eb114d2a481b35

omnilib-res: Add allow-reserved-package-id

Thx to the LineageOS team

https://github.com/LineageOS/android_lineage-sdk/blob/lineage-20.0/Android.bp#L44-L49

Change-Id: I07ba56685981a8735bdef5ea75528ec82dd68318
diff --git a/omnipreference/java/omnirom/preference/AppMultiSelectListPreference.java b/omnipreference/java/omnirom/preference/AppMultiSelectListPreference.java
new file mode 100644
index 0000000..008b77d
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/AppMultiSelectListPreference.java
@@ -0,0 +1,245 @@
+/*
+ *  Copyright (C) 2016 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package omnirom.preference;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import omnirom.preference.R;
+import com.android.settingslib.CustomDialogPreferenceCompat;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class AppMultiSelectListPreference extends CustomDialogPreferenceCompat {
+    private static final String TAG = "AppMultiSelectList";
+    private static final boolean DEBUG = false;
+
+    private final List<PackageItem> mPackageInfoList = new ArrayList<PackageItem>();
+    private AppListAdapter mAdapter;
+    private List<String> mValues = new ArrayList<String>();
+    private PackageManager mPm;
+
+    public AppMultiSelectListPreference(Context context) {
+        this(context, null);
+    }
+
+    public AppMultiSelectListPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setDialogLayoutResource(R.layout.preference_app_list);
+
+        mPm = context.getPackageManager();
+
+        final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        List<ResolveInfo> installedAppsInfo = getContext().getPackageManager().queryIntentActivities(
+                mainIntent, 0);
+
+        for (ResolveInfo info : installedAppsInfo) {
+            ComponentName componentName = new ComponentName(
+                    info.activityInfo.applicationInfo.packageName,
+                    info.activityInfo.name);
+
+            try {
+                final PackageItem item = new PackageItem(
+                        info.activityInfo.loadLabel(mPm), 0, componentName);
+                mPackageInfoList.add(item);
+            } catch (Exception e) {
+                if (DEBUG) Log.e(TAG, "Load installed apps", e);
+            }
+        }
+        Collections.sort(mPackageInfoList);
+
+        setPositiveButtonText(R.string.action_save);
+        setNegativeButtonText(android.R.string.cancel);
+    }
+
+    public void setValues(Collection<String> values) {
+        mValues.clear();
+        mValues.addAll(values);
+    }
+
+    public Collection<String> getValues() {
+        return mValues;
+    }
+
+    @Override
+    protected void onBindDialogView(View view) {
+        super.onBindDialogView(view);
+
+        mAdapter = new AppListAdapter(getContext());
+        final ListView listView = (ListView) view.findViewById(R.id.app_list);
+        listView.setAdapter(mAdapter);
+        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                final AppViewHolder holder = (AppViewHolder) view.getTag();
+                final boolean isChecked = !holder.checkBox.isChecked();
+
+                holder.checkBox.setChecked(isChecked);
+                PackageItem info = mAdapter.getItem(position);
+
+                if (isChecked) {
+                    mValues.add(info.mValue);
+                } else {
+                    mValues.remove(info.mValue);
+                }
+            }
+        });
+    }
+
+    @Override
+    protected void onDialogClosed(boolean positiveResult) {
+        super.onDialogClosed(positiveResult);
+        if (positiveResult) {
+            callChangeListener(mValues.size() > 0 ? mValues : null);
+        }
+    }
+
+    public class PackageItem implements Comparable<PackageItem> {
+        public final CharSequence mTitle;
+        public final int mAppIconResourceId;
+        public final ComponentName mComponentName;
+        public final String mValue;
+
+        PackageItem(CharSequence title, int iconResourceId, ComponentName componentName) {
+            mTitle = title;
+            mAppIconResourceId = iconResourceId;
+            mComponentName = componentName;
+            mValue = componentName.flattenToString();
+        }
+
+        PackageItem(CharSequence title, int iconResourceId, String value) {
+            mTitle = title;
+            mAppIconResourceId = iconResourceId;
+            mComponentName = null;
+            mValue = value;
+        }
+
+        @Override
+        public int compareTo(PackageItem another) {
+            return mTitle.toString().toUpperCase().compareTo(another.mTitle.toString().toUpperCase());
+        }
+
+        @Override
+        public int hashCode() {
+            return mValue.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object another) {
+            if (another == null || !(another instanceof PackageItem)) {
+                return false;
+            }
+            return mValue.equals(((PackageItem) another).mValue);
+        }
+    }
+
+    public class AppListAdapter extends ArrayAdapter<PackageItem> {
+        private final LayoutInflater mInflater;
+
+        public AppListAdapter(Context context) {
+            super(context, 0);
+            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            addAll(mPackageInfoList);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            AppViewHolder holder = AppViewHolder.createOrRecycle(mInflater, convertView);
+            convertView = holder.rootView;
+            PackageItem info = getItem(position);
+            holder.appName.setText(info.mTitle);
+            if (info.mAppIconResourceId != 0) {
+                holder.appIcon.setImageResource(info.mAppIconResourceId);
+            } else {
+                Drawable d = resolveAppIcon(info);
+                holder.appIcon.setImageDrawable(d);
+            }
+            holder.checkBox.setChecked(mValues.contains(info.mValue));
+            return convertView;
+        }
+
+        @Override
+        public PackageItem getItem(int position) {
+            return mPackageInfoList.get(position);
+        }
+    }
+
+    public static class AppViewHolder {
+        public View rootView;
+        public TextView appName;
+        public ImageView appIcon;
+        public CheckBox checkBox;
+
+        public static AppViewHolder createOrRecycle(LayoutInflater inflater, View convertView) {
+            if (convertView == null) {
+                convertView = inflater.inflate(R.layout.app_select_item, null);
+
+                // Creates a ViewHolder and store references to the two children views
+                // we want to bind data to.
+                AppViewHolder holder = new AppViewHolder();
+                holder.rootView = convertView;
+                holder.appName = (TextView) convertView.findViewById(R.id.app_name);
+                holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
+                holder.checkBox = (CheckBox) convertView.findViewById(android.R.id.checkbox);
+                convertView.setTag(holder);
+                return holder;
+            } else {
+                // Get the ViewHolder back to get fast access to the TextView
+                // and the ImageView.
+                return (AppViewHolder) convertView.getTag();
+            }
+        }
+    }
+
+    private Drawable getDefaultActivityIcon() {
+        return getContext().getResources().getDrawable(android.R.drawable.sym_def_app_icon);
+    }
+
+    private Drawable resolveAppIcon(PackageItem item) {
+        Drawable appIcon = null;
+        try {
+            appIcon = mPm.getActivityIcon(item.mComponentName);
+        } catch (PackageManager.NameNotFoundException e) {
+            if (DEBUG) Log.e(TAG, "resolveAppIcon", e);
+        }
+        if (appIcon == null) {
+            appIcon = getDefaultActivityIcon();
+        }
+        return appIcon;
+    }
+}
+
diff --git a/omnipreference/java/omnirom/preference/AppSelectListPreference.java b/omnipreference/java/omnirom/preference/AppSelectListPreference.java
new file mode 100644
index 0000000..86c3152
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/AppSelectListPreference.java
@@ -0,0 +1,339 @@
+/*
+* Copyright (C) 2017 The OmniROM Project
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+package omnirom.preference;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import androidx.preference.PreferenceViewHolder;
+import androidx.preference.PreferenceDialogFragment;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.android.settingslib.CustomDialogPreferenceCompat;
+import omnirom.preference.R;
+
+public class AppSelectListPreference extends CustomDialogPreferenceCompat {
+
+    private static String TAG = "AppSelectListPreference";
+    public static final String DISABLED_ENTRY = "disabled";
+
+    private AppSelectListAdapter mAdapter;
+    private Drawable mAppIconDrawable;
+    private int mAppIconResourceId;
+    private CharSequence mTitle;
+    private String mValue;
+    private PackageManager mPm;
+
+    public class PackageItem implements Comparable<PackageItem> {
+        public final CharSequence mTitle;
+        public final int mAppIconResourceId;
+        public final ComponentName mComponentName;
+        public final String mValue;
+
+        PackageItem(CharSequence title, int iconResourceId, ComponentName componentName) {
+            mTitle = title;
+            mAppIconResourceId = iconResourceId;
+            mComponentName = componentName;
+            mValue = componentName.flattenToString();
+        }
+
+        PackageItem(CharSequence title, int iconResourceId, String value) {
+            mTitle = title;
+            mAppIconResourceId = iconResourceId;
+            mComponentName = null;
+            mValue = value;
+        }
+
+        @Override
+        public int compareTo(PackageItem another) {
+            return mTitle.toString().toUpperCase().compareTo(another.mTitle.toString().toUpperCase());
+        }
+
+        @Override
+        public int hashCode() {
+            return mValue.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object another) {
+            if (another == null || !(another instanceof PackageItem)) {
+                return false;
+            }
+            return mValue.equals(((PackageItem) another).mValue);
+        }
+    }
+
+    public class AppSelectListAdapter extends BaseAdapter implements Runnable {
+        private LayoutInflater mInflater;
+        private List<PackageItem> mInstalledPackages = new LinkedList<PackageItem>();
+
+        private final Handler mHandler = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                PackageItem disabledItem = new PackageItem(getContext().getResources().getString(R.string.disabled_entry),
+                        R.drawable.ic_disabled, DISABLED_ENTRY);
+                mInstalledPackages.add(0, disabledItem);
+
+                notifyDataSetChanged();
+                updatePreferenceViews();
+            }
+        };
+
+        public AppSelectListAdapter(Context context) {
+            mInflater = LayoutInflater.from(context);
+            reloadList();
+        }
+
+        @Override
+        public int getCount() {
+            return mInstalledPackages.size();
+        }
+
+        @Override
+        public PackageItem getItem(int position) {
+            return mInstalledPackages.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return mInstalledPackages.get(position).hashCode();
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            ViewHolder holder;
+            if (convertView != null) {
+                holder = (ViewHolder) convertView.getTag();
+            } else {
+                convertView = mInflater.inflate(R.layout.applist_preference_icon, null, false);
+                holder = new ViewHolder();
+                convertView.setTag(holder);
+                holder.title = (TextView) convertView.findViewById(R.id.title);
+                holder.icon = (ImageView) convertView.findViewById(R.id.icon);
+            }
+
+            PackageItem applicationInfo = getItem(position);
+            holder.title.setText(applicationInfo.mTitle);
+            if (applicationInfo.mAppIconResourceId != 0) {
+                holder.icon.setImageResource(applicationInfo.mAppIconResourceId);
+            } else {
+                Drawable d = resolveAppIcon(applicationInfo);
+                holder.icon.setImageDrawable(d);
+            }
+            return convertView;
+        }
+
+        private void reloadList() {
+            mInstalledPackages.clear();
+            new Thread(this).start();
+        }
+
+        @Override
+        public void run() {
+            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+            List<ResolveInfo> installedAppsInfo = mPm.queryIntentActivities(mainIntent, 0);
+
+            for (ResolveInfo info : installedAppsInfo) {
+                ActivityInfo activity = info.activityInfo;
+                ApplicationInfo appInfo = activity.applicationInfo;
+                ComponentName componentName = new ComponentName(appInfo.packageName, activity.name);
+                CharSequence label = null;
+                try {
+                    label = activity.loadLabel(mPm);
+                } catch (Exception e) {
+                }
+                if (label != null) {
+                    final PackageItem item = new PackageItem(activity.loadLabel(mPm), 0, componentName);
+                    mInstalledPackages.add(item);
+                }
+            }
+            Collections.sort(mInstalledPackages);
+            mHandler.obtainMessage(0).sendToTarget();
+        }
+
+        private PackageItem resolveApplication(ComponentName componentName) {
+            for (PackageItem item : mInstalledPackages) {
+                if (item.mComponentName != null && item.mComponentName.equals(componentName)) {
+                    return item;
+                }
+            }
+            return null;
+        }
+
+        private class ViewHolder {
+            TextView title;
+            TextView summary;
+            ImageView icon;
+        }
+    }
+
+    public AppSelectListPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public AppSelectListPreference(Context context, int color) {
+        super(context, null);
+        init();
+    }
+
+    private void init() {
+        mPm = getContext().getPackageManager();
+        setDialogLayoutResource(R.layout.preference_app_list);
+        setLayoutResource(R.layout.preference_app_select);
+        setNegativeButtonText(android.R.string.cancel);
+        setPositiveButtonText(null);
+        setDialogTitle(R.string.choose_app);
+        setDialogIcon(null);
+        mAdapter = new AppSelectListAdapter(getContext());
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
+        super.onSetInitialValue(restorePersistedValue, defaultValue);
+        if (mTitle != null) {
+            setSummary(mTitle);
+        } else {
+            setSummary(getContext().getResources().getString(R.string.not_ready_summary));
+        }
+        mAppIconResourceId = R.drawable.ic_disabled;
+        setIcon(mAppIconResourceId);
+    }
+
+    @Override
+    protected void onBindDialogView(View view) {
+        super.onBindDialogView(view);
+
+        final ListView list = (ListView) view.findViewById(R.id.app_list);
+        list.setAdapter(mAdapter);
+        list.setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                PackageItem info = (PackageItem) parent.getItemAtPosition(position);
+                mValue = info.mValue;
+                if (shouldPersist()) {
+                    persistString(mValue);
+                }
+                mTitle = info.mTitle;
+                mAppIconDrawable = null;
+                mAppIconResourceId = 0;
+                if (info.mComponentName != null) {
+                    mAppIconDrawable = resolveAppIcon(info);
+                } else {
+                    mAppIconResourceId = info.mAppIconResourceId;
+                }
+
+                updatePreferenceViews();
+                callChangeListener(mValue);
+                getDialog().dismiss();
+            }
+        });
+    }
+
+    private void updatePreferenceViews() {
+        String name = null;
+        if (shouldPersist()) {
+            name = getPersistedString(null);
+        } else {
+            name = mValue;
+        }
+        mAppIconResourceId = R.drawable.ic_disabled;
+
+        if (name != null) {
+            if (name.equals(DISABLED_ENTRY)) {
+                mTitle = getContext().getResources().getString(R.string.disabled_entry);
+                mAppIconResourceId = R.drawable.ic_disabled;
+            } else {
+                mAppIconDrawable = null;
+                ComponentName componentName = ComponentName.unflattenFromString(name);
+                PackageItem item = mAdapter.resolveApplication(componentName);
+                if (item != null) {
+                    mTitle = item.mTitle;
+                    mAppIconDrawable = resolveAppIcon(item);
+                } else {
+                    mTitle = getContext().getResources().getString(R.string.resolve_failed_summary);
+                }
+            }
+        } else {
+            mTitle = getContext().getResources().getString(R.string.disabled_entry);
+            mAppIconResourceId = R.drawable.ic_disabled;
+        }
+
+        setSummary(mTitle);
+        if (mAppIconDrawable != null) {
+            setIcon(mAppIconDrawable);
+        } else {
+            setIcon(mAppIconResourceId);
+        }
+    }
+
+    private Drawable getDefaultActivityIcon() {
+        return getContext().getResources().getDrawable(android.R.drawable.sym_def_app_icon);
+    }
+
+    public String getValue() {
+        return mValue;
+    }
+
+    public void setValue(String value) {
+        mValue = value;
+    }
+
+    private Drawable resolveAppIcon(PackageItem item) {
+        Drawable appIcon = null;
+        try {
+            appIcon = mPm.getActivityIcon(item.mComponentName);
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+        if (appIcon == null) {
+            appIcon = getDefaultActivityIcon();
+        }
+        return appIcon;
+    }
+
+    public static class AppSelectListPreferenceDialogFragment
+            extends CustomDialogPreferenceCompat.CustomPreferenceDialogFragment {
+        public static CustomDialogPreferenceCompat.CustomPreferenceDialogFragment
+                newInstance(String key) {
+            return CustomDialogPreferenceCompat.CustomPreferenceDialogFragment.newInstance(key);
+        }
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/ColorSelectDialog.java b/omnipreference/java/omnirom/preference/ColorSelectDialog.java
new file mode 100644
index 0000000..ccd93f9
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/ColorSelectDialog.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ * Copyright (C) 2012 The CyanogenMod 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 omnirom.preference;
+
+import android.app.Activity;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.text.InputFilter;
+import android.text.InputFilter.LengthFilter;
+import android.util.Pair;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import omnirom.preference.R;
+import omnirom.ui.ColorPanelView;
+import omnirom.ui.ColorPickerView;
+import omnirom.ui.ColorPickerView.OnColorChangedListener;
+
+import java.util.ArrayList;
+import java.util.IllegalFormatException;
+import java.util.Locale;
+
+public class ColorSelectDialog extends AlertDialog implements
+        ColorPickerView.OnColorChangedListener, TextWatcher, OnFocusChangeListener {
+
+    private static final String TAG = "ColorSelectDialog";
+    private final static String STATE_KEY_COLOR = "BatteryLightDialog:color";
+
+    private ColorPickerView mColorPicker;
+
+    private EditText mHexColorInput;
+    private ColorPanelView mNewColor;
+    private LayoutInflater mInflater;
+    private boolean mMultiColor = true;
+    private Spinner mColorList;
+    private LinearLayout mColorListView;
+    private LinearLayout mColorPanelView;
+    private ColorPanelView mNewListColor;
+    private LedColorAdapter mLedColorAdapter;
+    private boolean mWithAlpha;
+
+    private boolean mShowLedPreview;
+    private boolean mShowMultiColor;
+    private NotificationManager mNoMan;
+    private Context mContext;
+
+    protected ColorSelectDialog(Context context, int initialColor, boolean showMultiColor, boolean showLedPreview, boolean withAlpha) {
+        super(context);
+        mContext = context;
+        mShowLedPreview = showLedPreview;
+        mWithAlpha = withAlpha;
+        mShowMultiColor = showMultiColor;
+        mMultiColor =
+                (getContext().getResources().getBoolean(R.bool.config_has_multi_color_led) || mShowMultiColor);
+        init(initialColor);
+    }
+
+    private void init(int color) {
+        // To fight color banding.
+        getWindow().setFormat(PixelFormat.RGBA_8888);
+        setUp(color);
+    }
+
+    /**
+     * This function sets up the dialog with the proper values.  If the speedOff parameters
+     * has a -1 value disable both spinners
+     *
+     * @param color - the color to set
+     * @param speedOn - the flash time in ms
+     * @param speedOff - the flash length in ms
+     */
+    private void setUp(int color) {
+        mInflater = (LayoutInflater) getContext()
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        View layout = mInflater.inflate(R.layout.dialog_battery_settings, null);
+
+        mNoMan = (NotificationManager)
+                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+        mColorPicker = (ColorPickerView) layout.findViewById(R.id.color_picker_view);
+        mHexColorInput = (EditText) layout.findViewById(R.id.hex_color_input);
+        mNewColor = (ColorPanelView) layout.findViewById(R.id.color_panel);
+        mColorPanelView = (LinearLayout) layout.findViewById(R.id.color_panel_view);
+
+        mColorListView = (LinearLayout) layout.findViewById(R.id.color_list_view);
+        mColorList = (Spinner) layout.findViewById(R.id.color_list_spinner);
+        mNewListColor = (ColorPanelView) layout.findViewById(R.id.color_list_panel);
+
+        mColorPicker.setOnColorChangedListener(this);
+        mHexColorInput.setOnFocusChangeListener(this);
+        setAlphaSliderVisible(mWithAlpha);
+        mColorPicker.setColor(color, true);
+        showLed(color);
+
+        mColorList = (Spinner) layout.findViewById(R.id.color_list_spinner);
+        mLedColorAdapter = new LedColorAdapter(
+                R.array.entries_led_colors,
+                R.array.values_led_colors);
+        mColorList.setAdapter(mLedColorAdapter);
+        mColorList.setSelection(mLedColorAdapter.getColorPosition(color));
+        mColorList.setOnItemSelectedListener(mColorListListener);
+
+        setView(layout);
+
+        // show and hide the correct UI depending if we have multi-color led or not
+        if (mMultiColor){
+            mColorListView.setVisibility(View.GONE);
+            mColorPicker.setVisibility(View.VISIBLE);
+            mColorPanelView.setVisibility(View.VISIBLE);
+        } else {
+            mColorListView.setVisibility(View.VISIBLE);
+            mColorPicker.setVisibility(View.GONE);
+            mColorPanelView.setVisibility(View.GONE);
+        }
+    }
+
+    private AdapterView.OnItemSelectedListener mColorListListener = new AdapterView.OnItemSelectedListener() {
+
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            int color = mLedColorAdapter.getColor(position);
+            mNewListColor.setColor(color);
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+        }
+    };
+
+    @Override
+    public Bundle onSaveInstanceState() {
+        Bundle state = super.onSaveInstanceState();
+        state.putInt(STATE_KEY_COLOR, getColor());
+        switchOffLed();
+        return state;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Bundle state) {
+        super.onRestoreInstanceState(state);
+        mColorPicker.setColor(state.getInt(STATE_KEY_COLOR), true);
+    }
+
+    @Override
+    public void onColorChanged(int color) {
+        final boolean hasAlpha = mWithAlpha;
+        final String format = hasAlpha ? "%08x" : "%06x";
+        final int mask = hasAlpha ? 0xFFFFFFFF : 0x00FFFFFF;
+
+        mNewColor.setColor(color);
+        mHexColorInput.setText(String.format(Locale.US, format, color & mask));
+
+        showLed(color);
+    }
+
+    private void showLed(int color) {
+        /*if (mShowLedPreview) {
+            if (color == 0xFFFFFFFF) {
+                // argb white doesn't work
+                color = 0xffffff;
+            }
+            mNoMan.forceShowLedLight(color);
+        }*/
+    }
+
+    public void switchOffLed() {
+        /*if (mShowLedPreview) {
+            mNoMan.forceShowLedLight(-1);
+        }*/
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        switchOffLed();
+    }
+
+    public void setAlphaSliderVisible(boolean visible) {
+        mHexColorInput.setFilters(new InputFilter[] { new InputFilter.LengthFilter(visible ? 8 : 6) } );
+        mColorPicker.setAlphaSliderVisible(visible);
+    }
+
+    public int getColor() {
+        if (mMultiColor){
+            return mColorPicker.getColor();
+        } else {
+            return mNewListColor.getColor();
+        }
+    }
+
+    class LedColorAdapter extends BaseAdapter implements SpinnerAdapter {
+        private ArrayList<Pair<String, Integer>> mColors;
+
+        public LedColorAdapter(int ledColorResource, int ledValueResource) {
+            mColors = new ArrayList<Pair<String, Integer>>();
+
+            String[] color_names = getContext().getResources().getStringArray(ledColorResource);
+            String[] color_values = getContext().getResources().getStringArray(ledValueResource);
+
+            for(int i = 0; i < color_values.length; ++i) {
+                try {
+                    int color = Color.parseColor(color_values[i]);
+                    mColors.add(new Pair<String, Integer>(color_names[i], color));
+                } catch (IllegalArgumentException ex) {
+                    // Number format is incorrect, ignore entry
+                }
+            }
+        }
+
+        /**
+         * Will return the position of the spinner entry with the specified
+         * color. Returns 0 if there is no such entry.
+         */
+        public int getColorPosition(int color) {
+            for (int position = 0; position < getCount(); ++position) {
+                if (getItem(position).second.equals(color)) {
+                    return position;
+                }
+            }
+
+            return 0;
+        }
+
+        public int getColor(int position) {
+            Pair<String, Integer> item = getItem(position);
+            if (item != null){
+                return item.second;
+            }
+
+            // -1 is white
+            return -1;
+        }
+
+        @Override
+        public int getCount() {
+            return mColors.size();
+        }
+
+        @Override
+        public Pair<String, Integer> getItem(int position) {
+            return mColors.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View view, ViewGroup parent) {
+            if (view == null) {
+                view = mInflater.inflate(R.layout.led_color_item, null);
+            }
+
+            Pair<String, Integer> entry = getItem(position);
+            ((TextView) view.findViewById(R.id.textViewName)).setText(entry.first);
+
+            return view;
+        }
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        String hexColor = mHexColorInput.getText().toString();
+        if (!hexColor.isEmpty()) {
+            try {
+                int color = Color.parseColor('#' + hexColor);
+                if (!mWithAlpha) {
+                    color |= 0xFF000000; // set opaque
+                }
+                mColorPicker.setColor(color);
+                mNewColor.setColor(color);
+            } catch (IllegalArgumentException ex) {
+                // Number format is incorrect, ignore
+            }
+        }
+    }
+
+    @Override
+    public void onFocusChange(View v, boolean hasFocus) {
+        if (!hasFocus) {
+            mHexColorInput.removeTextChangedListener(this);
+            InputMethodManager inputMethodManager = (InputMethodManager) getContext()
+                    .getSystemService(Activity.INPUT_METHOD_SERVICE);
+            inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
+        } else {
+            mHexColorInput.addTextChangedListener(this);
+        }
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/ColorSelectPreference.java b/omnipreference/java/omnirom/preference/ColorSelectPreference.java
new file mode 100644
index 0000000..97a39d5
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/ColorSelectPreference.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod 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 omnirom.preference;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RectShape;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.ImageView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import omnirom.preference.R;
+
+public class ColorSelectPreference extends Preference implements DialogInterface.OnDismissListener {
+
+    private static String TAG = "ColorSelectPreference";
+    public static final int DEFAULT_COLOR = 0xFFFFFF; //White
+
+    private ImageView mLightColorView;
+    private Resources mResources;
+    protected int mColorValue;
+    private AlertDialog mDialog;
+
+    private boolean mShowLedPreview;
+    private boolean mShowMultiColor;
+    private boolean mWithAlpha;
+
+    /**
+     * @param context
+     * @param attrs
+     */
+    public ColorSelectPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mColorValue = DEFAULT_COLOR;
+        init(context, attrs);
+    }
+
+    public ColorSelectPreference(Context context, int color) {
+        super(context, null);
+        mColorValue = color;
+        init(context, null);
+    }
+
+    public ColorSelectPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mColorValue = DEFAULT_COLOR;
+        init(context, attrs);
+    }
+
+    protected void init(Context context, AttributeSet attrs) {
+        setLayoutResource(R.layout.preference_color_select);
+        mResources = getContext().getResources();
+        if (attrs != null) {
+            final TypedArray attributes = context.obtainStyledAttributes(attrs,
+                    R.styleable.ColorSelectPreference);
+            final TypedValue useWithAlpha = attributes.peekValue(
+                    R.styleable.ColorSelectPreference_withAlpha);
+            if (useWithAlpha != null) {
+                mWithAlpha = (useWithAlpha.type == TypedValue.TYPE_INT_BOOLEAN
+                                && useWithAlpha.data != 0);
+            }
+            final TypedValue useLedPreview = attributes.peekValue(
+                    R.styleable.ColorSelectPreference_ledPreview);
+            if (useLedPreview != null) {
+                mShowLedPreview = (useLedPreview.type == TypedValue.TYPE_INT_BOOLEAN
+                                && useLedPreview.data != 0);
+            }
+            final TypedValue useMultiColor = attributes.peekValue(
+                    R.styleable.ColorSelectPreference_multiColor);
+            if (useMultiColor != null) {
+                mShowMultiColor = (useMultiColor.type == TypedValue.TYPE_INT_BOOLEAN
+                                && useMultiColor.data != 0);
+            }
+        }
+    }
+
+    public void setColor(int color) {
+        mColorValue = color;
+        updatePreferenceViews();
+    }
+
+    public int getColor() {
+        return mColorValue;
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        mLightColorView = (ImageView) holder.findViewById(R.id.light_color);
+
+        updatePreferenceViews();
+    }
+
+    private void updatePreferenceViews() {
+        final int width = (int) mResources.getDimension(R.dimen.color_preference_width);
+        final int height = (int) mResources.getDimension(R.dimen.color_preference_height);
+
+        if (mLightColorView != null) {
+            mLightColorView.setEnabled(true);
+            mLightColorView.setImageDrawable(createRectShape(width, height, 0xFF000000 | mColorValue));
+        }
+    }
+
+    @Override
+    protected void onClick() {
+        if (mDialog != null && mDialog.isShowing()) return;
+        mDialog = getDialog();
+        mDialog.setOnDismissListener(this);
+        mDialog.show();
+    }
+
+    public AlertDialog getDialog() {
+        final ColorSelectDialog d = new ColorSelectDialog(getContext(),
+                0xFF000000 | mColorValue, mShowMultiColor, mShowLedPreview, mWithAlpha);
+
+        d.setButton(AlertDialog.BUTTON_POSITIVE, mResources.getString(android.R.string.ok),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                int color = 0;
+                if (mShowLedPreview) {
+                    color =  d.getColor() & 0x00FFFFFF; // strip alpha, led does not support it
+                    d.switchOffLed();
+                } else {
+                    color =  d.getColor();
+                }
+                setColor(color);
+                callChangeListener(new Integer(color));
+            }
+        });
+        d.setButton(AlertDialog.BUTTON_NEGATIVE, mResources.getString(android.R.string.cancel),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                d.switchOffLed();
+            }
+        });
+
+        return d;
+    }
+
+    private static ShapeDrawable createRectShape(int width, int height, int color) {
+        ShapeDrawable shape = new ShapeDrawable(new RectShape());
+        shape.setIntrinsicHeight(height);
+        shape.setIntrinsicWidth(width);
+        shape.getPaint().setColor(color);
+        return shape;
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        mDialog = null;
+    }
+
+    public void setWithAlpha(boolean value) {
+        mWithAlpha = value;
+    }
+
+    public void setWithLedPreview(boolean value) {
+        mShowLedPreview = value;
+    }
+
+    public void setWithMultiColor(boolean value) {
+        mShowMultiColor = value;
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/GlobalCheckBoxPreference.java b/omnipreference/java/omnirom/preference/GlobalCheckBoxPreference.java
new file mode 100644
index 0000000..01a9e69
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/GlobalCheckBoxPreference.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod 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 omnirom.preference;
+
+import android.content.Context;
+import androidx.preference.CheckBoxPreference;
+import android.provider.Settings;
+import android.util.AttributeSet;
+
+public class GlobalCheckBoxPreference extends CheckBoxPreference {
+    public GlobalCheckBoxPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public GlobalCheckBoxPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public GlobalCheckBoxPreference(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    protected boolean persistBoolean(boolean value) {
+        if (shouldPersist()) {
+            if (value == getPersistedBoolean(!value)) {
+                // It's already there, so the same as persisting
+                return true;
+            }
+
+            Settings.Global.putInt(getContext().getContentResolver(), getKey(), value ? 1 : 0);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean getPersistedBoolean(boolean defaultReturnValue) {
+        if (!shouldPersist()) {
+            return defaultReturnValue;
+        }
+
+        return Settings.Global.getInt(getContext().getContentResolver(),
+                getKey(), defaultReturnValue ? 1 : 0) != 0;
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setChecked(Settings.Global.getString(getContext().getContentResolver(), getKey()) != null ? getPersistedBoolean(isChecked())
+                : (Boolean) defaultValue);
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/GlobalSettingSwitchPreference.java b/omnipreference/java/omnirom/preference/GlobalSettingSwitchPreference.java
new file mode 100644
index 0000000..1996a4b
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/GlobalSettingSwitchPreference.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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 omnirom.preference;
+
+import android.content.Context;
+import androidx.preference.SwitchPreference;
+import android.provider.Settings;
+import android.util.AttributeSet;
+
+public class GlobalSettingSwitchPreference extends SwitchPreference {
+    public GlobalSettingSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public GlobalSettingSwitchPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public GlobalSettingSwitchPreference(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    protected boolean persistBoolean(boolean value) {
+        if (shouldPersist()) {
+            if (value == getPersistedBoolean(!value)) {
+                // It's already there, so the same as persisting
+                return true;
+            }
+            Settings.Global.putInt(getContext().getContentResolver(), getKey(), value ? 1 : 0);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean getPersistedBoolean(boolean defaultReturnValue) {
+        if (!shouldPersist()) {
+            return defaultReturnValue;
+        }
+        return Settings.Global.getInt(getContext().getContentResolver(),
+                getKey(), defaultReturnValue ? 1 : 0) != 0;
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setChecked(Settings.Global.getString(getContext().getContentResolver(), getKey()) != null ? getPersistedBoolean(isChecked())
+                : (Boolean) defaultValue);
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/ScrollAppsViewPreference.java b/omnipreference/java/omnirom/preference/ScrollAppsViewPreference.java
new file mode 100644
index 0000000..bf84431
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/ScrollAppsViewPreference.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package omnirom.preference;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import omnirom.preference.R;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class ScrollAppsViewPreference extends Preference {
+    private static final String TAG = "ScrollAppsPreference";
+
+    private Context mContext;
+    private List<String> mValues = new ArrayList<String>();
+    private PackageManager mPm;
+    private LayoutInflater mInflater;
+
+    public ScrollAppsViewPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        initPreference(context);
+    }
+
+    public ScrollAppsViewPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initPreference(context);
+    }
+
+    public ScrollAppsViewPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initPreference(context);
+    }
+
+    public void setValues(Collection<String> values) {
+        mValues.clear();
+        mValues.addAll(values);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        LinearLayout linearLayout = (LinearLayout) holder.findViewById(R.id.selected_apps);
+        if (linearLayout.getChildCount() > 0) linearLayout.removeAllViews();
+
+        for (String value : mValues) {
+            try {
+                View v = mInflater.inflate(R.layout.app_grid_item, null);
+                ComponentName componentName = ComponentName.unflattenFromString(value);
+                Drawable icon = mPm.getActivityIcon(componentName);
+                ((ImageView) v.findViewById(R.id.appIcon)).setImageDrawable(icon);
+                v.setPadding(10, 5, 10, 5);
+                linearLayout.addView(v);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Set app icon", e);
+            }
+        }
+    }
+
+    private void initPreference(Context context) {
+        mContext = context;
+        setLayoutResource(R.layout.preference_selected_apps_view);
+        mPm = context.getPackageManager();
+        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/SecureCheckBoxPreference.java b/omnipreference/java/omnirom/preference/SecureCheckBoxPreference.java
new file mode 100644
index 0000000..5f281fb
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/SecureCheckBoxPreference.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod 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 omnirom.preference;
+
+import android.content.Context;
+import androidx.preference.CheckBoxPreference;
+import android.provider.Settings;
+import android.util.AttributeSet;
+
+public class SecureCheckBoxPreference extends CheckBoxPreference {
+    public SecureCheckBoxPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public SecureCheckBoxPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SecureCheckBoxPreference(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    protected boolean persistBoolean(boolean value) {
+        if (shouldPersist()) {
+            if (value == getPersistedBoolean(!value)) {
+                // It's already there, so the same as persisting
+                return true;
+            }
+
+            Settings.Secure.putInt(getContext().getContentResolver(), getKey(), value ? 1 : 0);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean getPersistedBoolean(boolean defaultReturnValue) {
+        if (!shouldPersist()) {
+            return defaultReturnValue;
+        }
+
+        return Settings.Secure.getInt(getContext().getContentResolver(),
+                getKey(), defaultReturnValue ? 1 : 0) != 0;
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setChecked(Settings.Secure.getString(getContext().getContentResolver(), getKey()) != null ? getPersistedBoolean(isChecked())
+                : (Boolean) defaultValue);
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/SecureSettingSwitchPreference.java b/omnipreference/java/omnirom/preference/SecureSettingSwitchPreference.java
new file mode 100644
index 0000000..0a33f2b
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/SecureSettingSwitchPreference.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod 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 omnirom.preference;
+
+import android.content.Context;
+import androidx.preference.SwitchPreference;
+import android.util.AttributeSet;
+
+import android.provider.Settings;
+
+public class SecureSettingSwitchPreference extends SwitchPreference {
+    public SecureSettingSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public SecureSettingSwitchPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SecureSettingSwitchPreference(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    protected boolean persistBoolean(boolean value) {
+        if (shouldPersist()) {
+            if (value == getPersistedBoolean(!value)) {
+                // It's already there, so the same as persisting
+                return true;
+            }
+            Settings.Secure.putInt(getContext().getContentResolver(), getKey(), value ? 1 : 0);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean getPersistedBoolean(boolean defaultReturnValue) {
+        if (!shouldPersist()) {
+            return defaultReturnValue;
+        }
+        return Settings.Secure.getInt(getContext().getContentResolver(),
+                getKey(), defaultReturnValue ? 1 : 0) != 0;
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setChecked(Settings.Secure.getString(getContext().getContentResolver(), getKey()) != null ? getPersistedBoolean(isChecked())
+                : (Boolean) defaultValue);
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/SeekBarPreference.java b/omnipreference/java/omnirom/preference/SeekBarPreference.java
new file mode 100644
index 0000000..b86a15c
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/SeekBarPreference.java
@@ -0,0 +1,221 @@
+/*
+ ** Copyright 2013, The ChameleonOS Open Source Project
+ ** Copyright 2016, The OmniROM 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 omnirom.preference;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.TypedValue;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import omnirom.preference.R;
+
+public class SeekBarPreference extends Preference implements OnSeekBarChangeListener {
+
+    private final String TAG = getClass().getName();
+
+    private static final String ANDROIDNS = "http://schemas.android.com/apk/res/android";
+    private static final int DEFAULT_VALUE = 50;
+
+    private int mMaxValue = 100;
+    private int mMinValue = 0;
+    private int mInterval = 1;
+    private int mCurrentValue;
+    private String mUnitsLeft = "";
+    private String mUnitsRight = "";
+    private SeekBar mSeekBar;
+    private TextView mStatusText;
+
+    public SeekBarPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initPreference(context, attrs);
+    }
+
+    public SeekBarPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        initPreference(context, attrs);
+    }
+
+    private void initPreference(Context context, AttributeSet attrs) {
+        setValuesFromXml(context, attrs);
+        setLayoutResource(R.layout.preference_seek_bar);
+    }
+
+    private void setValuesFromXml(Context context, AttributeSet attrs) {
+        mMaxValue = attrs.getAttributeIntValue(ANDROIDNS, "max", 100);
+
+        final TypedArray attributes = context.obtainStyledAttributes(attrs,
+                R.styleable.SeekBarPreference);
+
+        TypedValue minAttr =
+                attributes.peekValue(R.styleable.SeekBarPreference_min);
+        if (minAttr != null && minAttr.type == TypedValue.TYPE_INT_DEC) {
+            mMinValue = minAttr.data;
+        }
+
+        TypedValue unitsLeftAttr =
+                attributes.peekValue(R.styleable.SeekBarPreference_unitsLeft);
+        CharSequence data = null;
+        if (unitsLeftAttr != null && unitsLeftAttr.type == TypedValue.TYPE_STRING) {
+            if (unitsLeftAttr.resourceId != 0) {
+                data = context.getText(unitsLeftAttr.resourceId);
+            } else {
+                data = unitsLeftAttr.string;
+            }
+        }
+        mUnitsLeft = (data == null) ? "" : data.toString();
+
+        TypedValue unitsRightAttr =
+                attributes.peekValue(R.styleable.SeekBarPreference_unitsRight);
+        data = null;
+        if (unitsRightAttr != null && unitsRightAttr.type == TypedValue.TYPE_STRING) {
+            if (unitsRightAttr.resourceId != 0) {
+                data = context.getText(unitsRightAttr.resourceId);
+            } else {
+                data = unitsRightAttr.string;
+            }
+        }
+        mUnitsRight = (data == null) ? "" : data.toString();
+
+        TypedValue intervalAttr =
+                attributes.peekValue(R.styleable.SeekBarPreference_interval);
+        if (intervalAttr != null && intervalAttr.type == TypedValue.TYPE_INT_DEC) {
+            mInterval = intervalAttr.data;
+        }
+
+        attributes.recycle();
+    }
+
+    @Override
+    public void onDependencyChanged(Preference dependency, boolean disableDependent) {
+        super.onDependencyChanged(dependency, disableDependent);
+        this.setShouldDisableView(true);
+        if (mSeekBar != null) {
+            mSeekBar.setEnabled(!disableDependent);
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        mSeekBar = (SeekBar) holder.findViewById(R.id.seekbar);
+        mSeekBar.setMax(mMaxValue - mMinValue);
+        mSeekBar.setProgress(mCurrentValue - mMinValue);
+        mSeekBar.setOnSeekBarChangeListener(this);
+        mSeekBar.setEnabled(isEnabled());
+
+        mStatusText = (TextView) holder.findViewById(R.id.seekBarPrefValue);
+        mStatusText.setText(String.valueOf(mCurrentValue));
+        mStatusText.setMinimumWidth(30);
+
+        TextView unitsRight = (TextView) holder.findViewById(R.id.seekBarPrefUnitsRight);
+        unitsRight.setText(mUnitsRight);
+        TextView unitsLeft = (TextView) holder.findViewById(R.id.seekBarPrefUnitsLeft);
+        unitsLeft.setText(mUnitsLeft);
+    }
+
+    @Override
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+        int newValue = progress + mMinValue;
+
+        if (newValue > mMaxValue) {
+            newValue = mMaxValue;
+        } else if (newValue < mMinValue) {
+            newValue = mMinValue;
+        } else if (mInterval != 1 && newValue % mInterval != 0) {
+            newValue = Math.round(((float) newValue) / mInterval) * mInterval;
+        }
+
+        // change rejected, revert to the previous value
+        if (!callChangeListener(newValue)) {
+            seekBar.setProgress(mCurrentValue - mMinValue);
+            return;
+        }
+
+        // change accepted, store it
+        mCurrentValue = newValue;
+        mStatusText.setText(String.valueOf(newValue));
+        persistInt(newValue);
+    }
+
+    @Override
+    public void onStartTrackingTouch(SeekBar seekBar) {
+    }
+
+    @Override
+    public void onStopTrackingTouch(SeekBar seekBar) {
+        notifyChanged();
+    }
+
+    @Override
+    protected Object onGetDefaultValue(TypedArray ta, int index) {
+        int defaultValue = ta.getInt(index, DEFAULT_VALUE);
+        return defaultValue;
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        if (restoreValue) {
+            mCurrentValue = getPersistedInt(mCurrentValue);
+        } else {
+            int temp = 0;
+            try {
+                temp = (Integer) defaultValue;
+            } catch (Exception ex) {
+                Log.e(TAG, "Invalid default value: " + defaultValue.toString());
+            }
+            persistInt(temp);
+            mCurrentValue = temp;
+        }
+    }
+
+    public void setValue(int value) {
+        mCurrentValue = value;
+    }
+
+    public void setMaxValue(int value) {
+        mMaxValue = value;
+        if (mSeekBar != null) {
+            mSeekBar.setMax(mMaxValue - mMinValue);
+        }
+    }
+
+    public void setMinValue(int value) {
+        mMinValue = value;
+        if (mSeekBar != null) {
+            mSeekBar.setMax(mMaxValue - mMinValue);
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        if (mSeekBar != null) {
+            mSeekBar.setEnabled(enabled);
+        }
+        super.setEnabled(enabled);
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/SystemCheckBoxPreference.java b/omnipreference/java/omnirom/preference/SystemCheckBoxPreference.java
new file mode 100644
index 0000000..f6340e8
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/SystemCheckBoxPreference.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod 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 omnirom.preference;
+
+import android.content.Context;
+import androidx.preference.CheckBoxPreference;
+import android.provider.Settings;
+import android.util.AttributeSet;
+
+public class SystemCheckBoxPreference extends CheckBoxPreference {
+    public SystemCheckBoxPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public SystemCheckBoxPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SystemCheckBoxPreference(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    protected boolean persistBoolean(boolean value) {
+        if (shouldPersist()) {
+            if (value == getPersistedBoolean(!value)) {
+                // It's already there, so the same as persisting
+                return true;
+            }
+
+            Settings.System.putInt(getContext().getContentResolver(), getKey(), value ? 1 : 0);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean getPersistedBoolean(boolean defaultReturnValue) {
+        if (!shouldPersist()) {
+            return defaultReturnValue;
+        }
+
+        return Settings.System.getInt(getContext().getContentResolver(),
+                getKey(), defaultReturnValue ? 1 : 0) != 0;
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setChecked(Settings.System.getString(getContext().getContentResolver(), getKey()) != null ? getPersistedBoolean(isChecked())
+                : (Boolean) defaultValue);
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/SystemSettingSwitchPreference.java b/omnipreference/java/omnirom/preference/SystemSettingSwitchPreference.java
new file mode 100644
index 0000000..647ce90
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/SystemSettingSwitchPreference.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod 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 omnirom.preference;
+
+import android.content.Context;
+import android.provider.Settings;
+import androidx.preference.SwitchPreference;
+import android.util.AttributeSet;
+
+public class SystemSettingSwitchPreference extends SwitchPreference {
+    public SystemSettingSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public SystemSettingSwitchPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SystemSettingSwitchPreference(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    protected boolean persistBoolean(boolean value) {
+        if (shouldPersist()) {
+            if (value == getPersistedBoolean(!value)) {
+                // It's already there, so the same as persisting
+                return true;
+            }
+            Settings.System.putInt(getContext().getContentResolver(), getKey(), value ? 1 : 0);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean getPersistedBoolean(boolean defaultReturnValue) {
+        if (!shouldPersist()) {
+            return defaultReturnValue;
+        }
+        return Settings.System.getInt(getContext().getContentResolver(),
+                getKey(), defaultReturnValue ? 1 : 0) != 0;
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setChecked(Settings.System.getString(getContext().getContentResolver(), getKey()) != null ? getPersistedBoolean(isChecked())
+                : (Boolean) defaultValue);
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/SystemSettingsColorSelectPreference.java b/omnipreference/java/omnirom/preference/SystemSettingsColorSelectPreference.java
new file mode 100644
index 0000000..2bffc66
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/SystemSettingsColorSelectPreference.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The OmniROM 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 omnirom.preference;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.util.AttributeSet;
+
+public class SystemSettingsColorSelectPreference extends ColorSelectPreference {
+    private int mDefaultColor = ColorSelectPreference.DEFAULT_COLOR;
+
+    public SystemSettingsColorSelectPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public SystemSettingsColorSelectPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SystemSettingsColorSelectPreference(Context context) {
+        super(context, null);
+    }
+
+    @Override
+    protected boolean persistInt(int value) {
+        if (!shouldPersist()) {
+            return false;
+        }
+
+        if (value == getPersistedInt(~value)) {
+            // It's already there, so the same as persisting
+            return true;
+        }
+
+        Settings.System.putInt(getContext().getContentResolver(), getKey(), value);
+        return true;
+    }
+
+    @Override
+    protected int getPersistedInt(int defaultReturnValue) {
+        if (!shouldPersist()) {
+            return defaultReturnValue;
+        }
+
+        return Settings.System.getInt(getContext().getContentResolver(),
+                getKey(), defaultReturnValue);
+    }
+
+    public void updateColor() {
+        super.setColor(getPersistedInt(mColorValue));
+    }
+
+    @Override
+    public void setColor(int color) {
+        super.setColor(color);
+        persistInt(color);
+    }
+
+    @Override
+    public void setDefaultValue(Object defaultValue) {
+        mDefaultColor = (Integer) defaultValue;
+        super.setDefaultValue(defaultValue);
+        super.setColor(getPersistedInt((Integer) defaultValue));
+    }
+
+    public void resetToDefaultValue() {
+        setColor(mDefaultColor);
+    }
+}
diff --git a/omnipreference/java/omnirom/preference/WifiSelectListPreference.java b/omnipreference/java/omnirom/preference/WifiSelectListPreference.java
new file mode 100644
index 0000000..8feba20
--- /dev/null
+++ b/omnipreference/java/omnirom/preference/WifiSelectListPreference.java
@@ -0,0 +1,172 @@
+/*
+ *  Copyright (C) 2016 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package omnirom.preference;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.net.wifi.WifiManager;
+import android.icu.text.Collator;
+
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiSavedConfigUtils;
+
+import omnirom.preference.R;
+import com.android.settingslib.CustomDialogPreferenceCompat;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Comparator;
+import java.util.Collections;
+
+public class WifiSelectListPreference extends CustomDialogPreferenceCompat {
+    private List<AccessPoint> mAPInfoList;
+    private ActionListAdapter mAdapter;
+    private List<String> mValues = new ArrayList<String>();
+    private WifiManager mWifiManager;
+    private static final Comparator<AccessPoint> SAVED_NETWORK_COMPARATOR =
+            new Comparator<AccessPoint>() {
+                final Collator mCollator = Collator.getInstance();
+
+                @Override
+                public int compare(AccessPoint ap1, AccessPoint ap2) {
+                    return mCollator.compare(
+                            nullToEmpty(ap1.getSsidStr()), nullToEmpty(ap2.getSsidStr()));
+                }
+
+                private String nullToEmpty(String string) {
+                    return (string == null) ? "" : string;
+                }
+            };
+
+    public WifiSelectListPreference(Context context) {
+        this(context, null);
+    }
+
+    public WifiSelectListPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setDialogLayoutResource(R.layout.preference_wifi_ap_list);
+        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        mAPInfoList = WifiSavedConfigUtils.getAllConfigs(context, mWifiManager);
+        Collections.sort(mAPInfoList, SAVED_NETWORK_COMPARATOR);
+
+        setPositiveButtonText(R.string.action_save);
+        setNegativeButtonText(android.R.string.cancel);
+    }
+
+    public void setValues(Collection<String> values) {
+        mValues.clear();
+        mValues.addAll(values);
+    }
+
+    public Collection<String> getValues() {
+        return mValues;
+    }
+
+    @Override
+    protected void onBindDialogView(View view) {
+        super.onBindDialogView(view);
+
+        mAdapter = new ActionListAdapter(getContext());
+        final ListView listView = (ListView) view.findViewById(R.id.wifi_ap_list);
+        listView.setAdapter(mAdapter);
+        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                final AppViewHolder holder = (AppViewHolder) view.getTag();
+                final boolean isChecked = !holder.checkBox.isChecked();
+
+                holder.checkBox.setChecked(isChecked);
+                AccessPoint info = mAdapter.getItem(position);
+
+                if (isChecked) {
+                    mValues.add(info.getSsidStr());
+                } else {
+                    mValues.remove(info.getSsidStr());
+                }
+            }
+        });
+    }
+
+    @Override
+    protected void onDialogClosed(boolean positiveResult) {
+        super.onDialogClosed(positiveResult);
+        if (positiveResult) {
+            callChangeListener(mValues.size() > 0 ? mValues : null);
+        }
+    }
+
+    public class ActionListAdapter extends ArrayAdapter<AccessPoint> {
+        private final LayoutInflater mInflater;
+
+        public ActionListAdapter(Context context) {
+            super(context, 0);
+            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            addAll(mAPInfoList);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            AppViewHolder holder = AppViewHolder.createOrRecycle(mInflater, convertView);
+            convertView = holder.rootView;
+            AccessPoint info = getItem(position);
+            holder.title.setText(info.getSsidStr());
+            holder.checkBox.setChecked(mValues.contains(info.getSsidStr()));
+            return convertView;
+        }
+
+        @Override
+        public AccessPoint getItem(int position) {
+            return mAPInfoList.get(position);
+        }
+    }
+
+    public static class AppViewHolder {
+        public View rootView;
+        public TextView title;
+        public CheckBox checkBox;
+
+        public static AppViewHolder createOrRecycle(LayoutInflater inflater, View convertView) {
+            if (convertView == null) {
+                convertView = inflater.inflate(R.layout.wifi_ap_item, null);
+
+                // Creates a ViewHolder and store references to the two children views
+                // we want to bind data to.
+                AppViewHolder holder = new AppViewHolder();
+                holder.rootView = convertView;
+                holder.title = (TextView) convertView.findViewById(R.id.ap_name);
+                holder.checkBox = (CheckBox) convertView.findViewById(android.R.id.checkbox);
+                convertView.setTag(holder);
+                return holder;
+            } else {
+                // Get the ViewHolder back to get fast access to the TextView
+                // and the ImageView.
+                return (AppViewHolder) convertView.getTag();
+            }
+        }
+    }
+}
+